backend v1 - styling to do
This commit is contained in:
parent
6e187943c4
commit
7c4f24c1b4
|
@ -39,7 +39,7 @@
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@beyonk/svelte-notifications": "^2.0.3",
|
"@beyonk/svelte-notifications": "^2.0.3",
|
||||||
"@budibase/bbui": "^1.4.1",
|
"@budibase/bbui": "^1.5.0",
|
||||||
"@budibase/client": "^0.0.32",
|
"@budibase/client": "^0.0.32",
|
||||||
"@nx-js/compiler-util": "^2.0.0",
|
"@nx-js/compiler-util": "^2.0.0",
|
||||||
"codemirror": "^5.51.0",
|
"codemirror": "^5.51.0",
|
||||||
|
|
|
@ -84,7 +84,6 @@
|
||||||
text-align: left;
|
text-align: left;
|
||||||
color: var(--ink);
|
color: var(--ink);
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
padding-left: 12px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.uk-text-right {
|
.uk-text-right {
|
||||||
|
|
|
@ -4,13 +4,16 @@ import api from "../api"
|
||||||
|
|
||||||
export const getBackendUiStore = () => {
|
export const getBackendUiStore = () => {
|
||||||
const INITIAL_BACKEND_UI_STATE = {
|
const INITIAL_BACKEND_UI_STATE = {
|
||||||
breadcrumbs: [],
|
|
||||||
models: [],
|
models: [],
|
||||||
views: [],
|
views: [],
|
||||||
users: [],
|
users: [],
|
||||||
selectedDatabase: {},
|
selectedDatabase: {},
|
||||||
selectedModel: {},
|
selectedModel: {},
|
||||||
draftModel: {}
|
draftModel: {},
|
||||||
|
tabs: {
|
||||||
|
SETUP_PANEL: "SETUP",
|
||||||
|
NAVIGATION_PANEL: "NAVIGATE"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const store = writable(INITIAL_BACKEND_UI_STATE)
|
const store = writable(INITIAL_BACKEND_UI_STATE)
|
||||||
|
@ -27,7 +30,6 @@ export const getBackendUiStore = () => {
|
||||||
if (models && models.length > 0) {
|
if (models && models.length > 0) {
|
||||||
store.actions.models.select(models[0]);
|
store.actions.models.select(models[0]);
|
||||||
}
|
}
|
||||||
state.breadcrumbs = [db.name]
|
|
||||||
state.models = models
|
state.models = models
|
||||||
state.views = views
|
state.views = views
|
||||||
return state
|
return state
|
||||||
|
@ -40,11 +42,6 @@ export const getBackendUiStore = () => {
|
||||||
state.selectedView = state.selectedView
|
state.selectedView = state.selectedView
|
||||||
return state
|
return state
|
||||||
}),
|
}),
|
||||||
view: record =>
|
|
||||||
store.update(state => {
|
|
||||||
state.breadcrumbs = [state.selectedDatabase.name, record._id]
|
|
||||||
return state
|
|
||||||
}),
|
|
||||||
select: record =>
|
select: record =>
|
||||||
store.update(state => {
|
store.update(state => {
|
||||||
state.selectedRecord = record
|
state.selectedRecord = record
|
||||||
|
@ -57,6 +54,7 @@ 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 ({ instanceId, model }) => {
|
save: async ({ instanceId, model }) => {
|
||||||
|
@ -102,6 +100,8 @@ export const getBackendUiStore = () => {
|
||||||
|
|
||||||
state.selectedField = field.name
|
state.selectedField = field.name
|
||||||
|
|
||||||
|
state.tabs.NAVIGATION_PANEL = "NAVIGATE"
|
||||||
|
|
||||||
return state
|
return state
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
|
@ -7,7 +7,6 @@
|
||||||
export let tertiary
|
export let tertiary
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<!-- TODO: Move to bbui -->
|
|
||||||
<div on:click class:primary class:secondary class:tertiary>
|
<div on:click class:primary class:secondary class:tertiary>
|
||||||
<i class={icon} />
|
<i class={icon} />
|
||||||
<span>{title}</span>
|
<span>{title}</span>
|
||||||
|
@ -15,8 +14,7 @@
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
div {
|
div {
|
||||||
width: 120px;
|
height: 80px;
|
||||||
height: 90px;
|
|
||||||
border-radius: 3px;
|
border-radius: 3px;
|
||||||
color: var(--ink);
|
color: var(--ink);
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
|
@ -26,6 +24,7 @@
|
||||||
align-items: center;
|
align-items: center;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
transition: 0.3s transform;
|
transition: 0.3s transform;
|
||||||
|
background: var(--light-grey);
|
||||||
}
|
}
|
||||||
|
|
||||||
i {
|
i {
|
||||||
|
@ -33,7 +32,8 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
span {
|
span {
|
||||||
font-size: 16px;
|
font-size: 14px;
|
||||||
|
text-align: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
div:hover {
|
div:hover {
|
||||||
|
@ -53,4 +53,5 @@
|
||||||
.tertiary {
|
.tertiary {
|
||||||
background: var(--white);
|
background: var(--white);
|
||||||
}
|
}
|
||||||
|
|
||||||
</style>
|
</style>
|
|
@ -3,8 +3,8 @@
|
||||||
export let label = ""
|
export let label = ""
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<input class="uk-checkbox" type="checkbox" bind:checked on:change />
|
|
||||||
{label}
|
{label}
|
||||||
|
<input class="uk-checkbox" type="checkbox" bind:checked on:change />
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
input {
|
input {
|
||||||
|
|
|
@ -13,13 +13,25 @@
|
||||||
let numberText = value === null || value === undefined ? "" : value.toString()
|
let numberText = value === null || value === undefined ? "" : value.toString()
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="uk-margin">
|
<div class="numberbox">
|
||||||
<label class="uk-form-label">{label}</label>
|
<label>{label}</label>
|
||||||
<div class="uk-form-controls">
|
<input
|
||||||
<input
|
class="budibase__input"
|
||||||
class="budibase__input"
|
type="number"
|
||||||
type="number"
|
{value}
|
||||||
{value}
|
on:change={inputChanged} />
|
||||||
on:change={inputChanged} />
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.numberbox {
|
||||||
|
display: grid;
|
||||||
|
align-items: center;
|
||||||
|
grid-template-columns: 40% 1fr;
|
||||||
|
margin-top: 8px;
|
||||||
|
margin-bottom: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
label {
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
|
@ -24,7 +24,7 @@
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
textarea {
|
textarea {
|
||||||
width: 300px;
|
width: 100%;
|
||||||
height: 100px;
|
height: 100px;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
@ -1,18 +1,7 @@
|
||||||
<script>
|
<script>
|
||||||
import { onMount, getContext } from "svelte"
|
import { onMount, getContext } from "svelte"
|
||||||
import { store, backendUiStore } from "builderStore"
|
import { store, backendUiStore } from "builderStore"
|
||||||
import {
|
import { Button } from "@budibase/bbui"
|
||||||
tap,
|
|
||||||
get,
|
|
||||||
find,
|
|
||||||
last,
|
|
||||||
compose,
|
|
||||||
flatten,
|
|
||||||
map,
|
|
||||||
remove,
|
|
||||||
keys,
|
|
||||||
takeRight,
|
|
||||||
} from "lodash/fp"
|
|
||||||
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 TablePagination from "./TablePagination.svelte"
|
import TablePagination from "./TablePagination.svelte"
|
||||||
|
@ -68,10 +57,22 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$: paginatedData = data ? data.slice(
|
$: paginatedData = data
|
||||||
currentPage * ITEMS_PER_PAGE,
|
? data.slice(
|
||||||
currentPage * ITEMS_PER_PAGE + ITEMS_PER_PAGE
|
currentPage * ITEMS_PER_PAGE,
|
||||||
) : []
|
currentPage * ITEMS_PER_PAGE + ITEMS_PER_PAGE
|
||||||
|
)
|
||||||
|
: []
|
||||||
|
|
||||||
|
const createNewRecord = () => {
|
||||||
|
open(
|
||||||
|
CreateEditRecordModal,
|
||||||
|
{
|
||||||
|
onClosed: close,
|
||||||
|
},
|
||||||
|
{ styleContent: { padding: "0" } }
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
onMount(() => {
|
onMount(() => {
|
||||||
if (views.length) {
|
if (views.length) {
|
||||||
|
@ -83,6 +84,12 @@
|
||||||
<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}>
|
||||||
|
<span class="button-inner">
|
||||||
|
<i class="ri-add-circle-fill" />
|
||||||
|
Create New Record
|
||||||
|
</span>
|
||||||
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
<table class="uk-table">
|
<table class="uk-table">
|
||||||
<thead>
|
<thead>
|
||||||
|
|
|
@ -1,156 +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) : []
|
|
||||||
$: instanceId = $backendUiStore.selectedDatabase._id
|
|
||||||
|
|
||||||
async function saveModel() {
|
|
||||||
const SAVE_MODEL_URL = `/api/${instanceId}/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 Model</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 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}
|
|
||||||
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-light);
|
|
||||||
}
|
|
||||||
|
|
||||||
.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-light);
|
|
||||||
margin-top: 40px;
|
|
||||||
padding: 20px 40px 20px 40px;
|
|
||||||
display: flex;
|
|
||||||
justify-content: flex-end;
|
|
||||||
}
|
|
||||||
</style>
|
|
|
@ -1,104 +0,0 @@
|
||||||
<script>
|
|
||||||
import Dropdown from "components/common/Dropdown.svelte"
|
|
||||||
import Textbox from "components/common/Textbox.svelte"
|
|
||||||
import Button from "components/common/Button.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 { keys, cloneDeep } from "lodash/fp"
|
|
||||||
|
|
||||||
const FIELD_TYPES = ["string", "number", "boolean"]
|
|
||||||
|
|
||||||
export let field = {
|
|
||||||
type: "string",
|
|
||||||
constraints: { type: "string", presence: false },
|
|
||||||
}
|
|
||||||
export let schema
|
|
||||||
export let goBack
|
|
||||||
|
|
||||||
let errors = []
|
|
||||||
let draftField = cloneDeep(field)
|
|
||||||
|
|
||||||
let type = field.type
|
|
||||||
let constraints = field.constraints
|
|
||||||
let required =
|
|
||||||
field.constraints.presence && !field.constraints.presence.allowEmpty
|
|
||||||
|
|
||||||
const save = () => {
|
|
||||||
constraints.presence = required ? { allowEmpty: false } : false
|
|
||||||
schema[field.name] = { type, constraints }
|
|
||||||
goBack()
|
|
||||||
}
|
|
||||||
|
|
||||||
$: constraints =
|
|
||||||
type === "string"
|
|
||||||
? { type: "string", length: {}, presence: false }
|
|
||||||
: type === "number"
|
|
||||||
? { type: "number", presence: false, numericality: {} }
|
|
||||||
: type === "boolean"
|
|
||||||
? { type: "boolean", presence: false }
|
|
||||||
: type === "datetime"
|
|
||||||
? { type: "date", datetime: {}, presence: false }
|
|
||||||
: type.startsWith("array")
|
|
||||||
? { type: "array", presence: false }
|
|
||||||
: { type: "string", presence: false }
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<div class="root">
|
|
||||||
|
|
||||||
<ErrorsBox {errors} />
|
|
||||||
|
|
||||||
<form on:submit|preventDefault class="uk-form-stacked">
|
|
||||||
<Textbox label="Name" bind:text={field.name} />
|
|
||||||
<Dropdown label="Type" bind:selected={type} options={FIELD_TYPES} />
|
|
||||||
|
|
||||||
<Checkbox label="Required" bind:checked={required} />
|
|
||||||
|
|
||||||
{#if type === 'string'}
|
|
||||||
<NumberBox label="Max Length" bind:value={constraints.length.maximum} />
|
|
||||||
<ValuesList label="Categories" bind:values={constraints.inclusion} />
|
|
||||||
{:else if type === 'datetime'}
|
|
||||||
<DatePicker
|
|
||||||
label="Min Value"
|
|
||||||
bind:value={constraints.datetime.earliest} />
|
|
||||||
<DatePicker label="Max Value" bind:value={constraints.datetime.latest} />
|
|
||||||
{:else if type === 'number'}
|
|
||||||
<NumberBox
|
|
||||||
label="Min Value"
|
|
||||||
bind:value={constraints.numericality.greaterThanOrEqualTo} />
|
|
||||||
<NumberBox
|
|
||||||
label="Max Value"
|
|
||||||
bind:value={constraints.numericality.lessThanOrEqualTo} />
|
|
||||||
{/if}
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
<footer>
|
|
||||||
<div class="button">
|
|
||||||
<ActionButton secondary on:click={goBack}>Cancel</ActionButton>
|
|
||||||
</div>
|
|
||||||
<ActionButton primary on:click={save}>Save</ActionButton>
|
|
||||||
</footer>
|
|
||||||
|
|
||||||
<style>
|
|
||||||
.root {
|
|
||||||
margin: 40px;
|
|
||||||
}
|
|
||||||
footer {
|
|
||||||
padding: 20px 40px;
|
|
||||||
border-radius: 0 0 5px 5px;
|
|
||||||
bottom: 0;
|
|
||||||
left: 0;
|
|
||||||
background: var(--grey-light);
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: flex-end;
|
|
||||||
}
|
|
||||||
|
|
||||||
.button {
|
|
||||||
margin-right: 20px;
|
|
||||||
}
|
|
||||||
</style>
|
|
|
@ -1,6 +1,7 @@
|
||||||
<script>
|
<script>
|
||||||
import { onMount } from "svelte"
|
import { onMount } from "svelte"
|
||||||
import { store, backendUiStore } from "builderStore"
|
import { store, backendUiStore } from "builderStore"
|
||||||
|
import { notifier } from "@beyonk/svelte-notifications";
|
||||||
import { compose, map, get, flatten } from "lodash/fp"
|
import { compose, map, get, flatten } from "lodash/fp"
|
||||||
import ActionButton from "components/common/ActionButton.svelte"
|
import ActionButton from "components/common/ActionButton.svelte"
|
||||||
import LinkedRecordSelector from "components/common/LinkedRecordSelector.svelte"
|
import LinkedRecordSelector from "components/common/LinkedRecordSelector.svelte"
|
||||||
|
@ -63,6 +64,7 @@
|
||||||
backendUiStore.update(state => {
|
backendUiStore.update(state => {
|
||||||
state.selectedView = state.selectedView
|
state.selectedView = state.selectedView
|
||||||
onClosed()
|
onClosed()
|
||||||
|
notifier.success("Record created successfully.");
|
||||||
return state
|
return state
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
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 CreateEditModelModal } from "./CreateEditModel/CreateEditModel.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 CreateDatabaseModal } from "./CreateDatabase.svelte"
|
||||||
export { default as CreateUserModal } from "./CreateUser.svelte"
|
export { default as CreateUserModal } from "./CreateUser.svelte"
|
||||||
|
|
|
@ -1,87 +0,0 @@
|
||||||
<script>
|
|
||||||
import { getContext } from "svelte"
|
|
||||||
import { store, backendUiStore } from "builderStore"
|
|
||||||
import HierarchyRow from "./HierarchyRow.svelte"
|
|
||||||
import DatabasesList from "./DatabasesList.svelte"
|
|
||||||
import UsersList from "./UsersList.svelte"
|
|
||||||
import NavItem from "./NavItem.svelte"
|
|
||||||
import getIcon from "components/common/icon"
|
|
||||||
import {
|
|
||||||
CreateDatabaseModal,
|
|
||||||
CreateUserModal,
|
|
||||||
} from "components/database/ModelDataTable/modals"
|
|
||||||
const { open, close } = getContext("simple-modal")
|
|
||||||
|
|
||||||
const openDatabaseCreator = () => {
|
|
||||||
open(
|
|
||||||
CreateDatabaseModal,
|
|
||||||
{
|
|
||||||
onClosed: close,
|
|
||||||
},
|
|
||||||
{ styleContent: { padding: "0" } }
|
|
||||||
)
|
|
||||||
}
|
|
||||||
const openUserCreator = () => {
|
|
||||||
open(
|
|
||||||
CreateUserModal,
|
|
||||||
{
|
|
||||||
onClosed: close,
|
|
||||||
},
|
|
||||||
{ styleContent: { padding: "0" } }
|
|
||||||
)
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<div class="items-root">
|
|
||||||
<div class="hierarchy" />
|
|
||||||
{#if $backendUiStore.selectedDatabase._id}
|
|
||||||
<div class="hierarchy">
|
|
||||||
<div class="components-list-container">
|
|
||||||
<div class="nav-group-header">
|
|
||||||
<div class="hierarchy-title">Users</div>
|
|
||||||
<i class="ri-add-line hoverable" on:click={openUserCreator} />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="hierarchy-items-container">
|
|
||||||
<UsersList />
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
{/if}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<style>
|
|
||||||
.items-root {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
max-height: 100%;
|
|
||||||
height: 100%;
|
|
||||||
background: var(--white);
|
|
||||||
}
|
|
||||||
|
|
||||||
.nav-group-header {
|
|
||||||
display: flex;
|
|
||||||
justify-content: space-between;
|
|
||||||
align-items: center;
|
|
||||||
padding: 20px 20px 10px 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.hierarchy-title {
|
|
||||||
align-items: center;
|
|
||||||
font-size: 18px;
|
|
||||||
font-weight: 700;
|
|
||||||
text-rendering: optimizeLegibility;
|
|
||||||
color: var(--ink);
|
|
||||||
}
|
|
||||||
|
|
||||||
.hierarchy {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
}
|
|
||||||
|
|
||||||
.hierarchy-items-container {
|
|
||||||
flex: 1 1 auto;
|
|
||||||
overflow-y: auto;
|
|
||||||
}
|
|
||||||
</style>
|
|
|
@ -4,7 +4,6 @@
|
||||||
import { cloneDeep } from "lodash/fp"
|
import { cloneDeep } from "lodash/fp"
|
||||||
import getIcon from "../common/icon"
|
import getIcon from "../common/icon"
|
||||||
import {
|
import {
|
||||||
CreateEditModelModal,
|
|
||||||
CreateEditViewModal,
|
CreateEditViewModal,
|
||||||
} from "components/database/ModelDataTable/modals"
|
} from "components/database/ModelDataTable/modals"
|
||||||
import api from "builderStore/api"
|
import api from "builderStore/api"
|
||||||
|
|
|
@ -1,47 +0,0 @@
|
||||||
<script>
|
|
||||||
export let title
|
|
||||||
export let icon
|
|
||||||
|
|
||||||
export let primary
|
|
||||||
export let secondary
|
|
||||||
export let tertiary
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<div on:click class:primary class:secondary class:tertiary>
|
|
||||||
<i class={icon} />
|
|
||||||
<span>{title}</span>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<style>
|
|
||||||
div {
|
|
||||||
height: 90px;
|
|
||||||
border-radius: 3px;
|
|
||||||
color: var(--ink);
|
|
||||||
padding: 20px;
|
|
||||||
display: flex;
|
|
||||||
justify-content: center;
|
|
||||||
align-items: center;
|
|
||||||
flex-direction: column;
|
|
||||||
}
|
|
||||||
|
|
||||||
span {
|
|
||||||
font-size: 16px;
|
|
||||||
}
|
|
||||||
|
|
||||||
div:hover {
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
|
|
||||||
.primary {
|
|
||||||
background: var(--ink);
|
|
||||||
color: var(--white);
|
|
||||||
}
|
|
||||||
|
|
||||||
.secondary {
|
|
||||||
background: var(--secondary);
|
|
||||||
}
|
|
||||||
|
|
||||||
.tertiary {
|
|
||||||
background: var(--white);
|
|
||||||
}
|
|
||||||
</style>
|
|
|
@ -1,7 +1,7 @@
|
||||||
<script>
|
<script>
|
||||||
import * as blockDefinitions from "constants/backend"
|
import * as blockDefinitions from "constants/backend"
|
||||||
import { backendUiStore } from "builderStore";
|
import { backendUiStore } from "builderStore";
|
||||||
import Block from "./Block.svelte"
|
import Block from "components/common/Block.svelte"
|
||||||
|
|
||||||
const HEADINGS = [
|
const HEADINGS = [
|
||||||
{
|
{
|
||||||
|
@ -13,8 +13,8 @@
|
||||||
key: "BLOCKS",
|
key: "BLOCKS",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: "Table",
|
title: "Model",
|
||||||
key: "TABLES",
|
key: "MODELS",
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@ -40,9 +40,6 @@
|
||||||
{#each Object.values(blockDefinitions[selectedTab]) as blockDefinition}
|
{#each Object.values(blockDefinitions[selectedTab]) as blockDefinition}
|
||||||
<Block
|
<Block
|
||||||
on:click={() => addField(blockDefinition)}
|
on:click={() => addField(blockDefinition)}
|
||||||
primary={true}
|
|
||||||
secondary={false}
|
|
||||||
tertiary={false}
|
|
||||||
title={blockDefinition.name}
|
title={blockDefinition.name}
|
||||||
icon={blockDefinition.icon} />
|
icon={blockDefinition.icon} />
|
||||||
{/each}
|
{/each}
|
||||||
|
@ -53,11 +50,15 @@
|
||||||
header {
|
header {
|
||||||
margin-top: 20px;
|
margin-top: 20px;
|
||||||
margin-bottom: 20px;
|
margin-bottom: 20px;
|
||||||
|
display: grid;
|
||||||
|
grid-gap: 20px;
|
||||||
|
grid-template-columns: repeat(3, 1fr);
|
||||||
}
|
}
|
||||||
|
|
||||||
span {
|
span {
|
||||||
margin-right: 20px;
|
text-align: center;
|
||||||
padding: 10px;
|
padding: 10px;
|
||||||
|
font-weight: 500;
|
||||||
border-radius: 3px;
|
border-radius: 3px;
|
||||||
color: var(--ink-lighter);
|
color: var(--ink-lighter);
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
|
|
|
@ -1,22 +1,23 @@
|
||||||
<script>
|
<script>
|
||||||
import { backendUiStore } from "builderStore";
|
import { backendUiStore } from "builderStore";
|
||||||
|
import { fade } from 'svelte/transition';
|
||||||
import { FIELDS, BLOCKS, MODELS } from "constants/backend";
|
import { FIELDS, BLOCKS, MODELS } from "constants/backend";
|
||||||
import Block from "./Block.svelte";
|
import Block from "components/common/Block.svelte";
|
||||||
|
|
||||||
function addNewField(field) {
|
function addNewField(field) {
|
||||||
backendUiStore.actions.models.addField(field);
|
backendUiStore.actions.models.addField(field);
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<section>
|
<section transition:fade>
|
||||||
<header>
|
<header>
|
||||||
<h2>Create New Table</h2>
|
<h2>Create New Model</h2>
|
||||||
<p>Before you can view your table, you need to set it up.</p>
|
<p>Before you can view your model, you need to set it up.</p>
|
||||||
</header>
|
</header>
|
||||||
|
|
||||||
<div class="block-row">
|
<div class="block-row">
|
||||||
<span class="block-row-title">Fields</span>
|
<span class="block-row-title">Fields</span>
|
||||||
<p>Blocks are pre-made fields and help you build your table quicker.</p>
|
<p>Blocks are pre-made fields and help you build your model quicker.</p>
|
||||||
<div class="blocks">
|
<div class="blocks">
|
||||||
{#each Object.values(FIELDS) as field}
|
{#each Object.values(FIELDS) as field}
|
||||||
<Block primary title={field.name} icon={field.icon} on:click={() => addNewField(field)} />
|
<Block primary title={field.name} icon={field.icon} on:click={() => addNewField(field)} />
|
||||||
|
@ -26,17 +27,17 @@
|
||||||
|
|
||||||
<div class="block-row">
|
<div class="block-row">
|
||||||
<span class="block-row-title">Blocks</span>
|
<span class="block-row-title">Blocks</span>
|
||||||
<p>Blocks are pre-made fields and help you build your table quicker.</p>
|
<p>Blocks are pre-made fields and help you build your model quicker.</p>
|
||||||
<div class="blocks">
|
<div class="blocks">
|
||||||
{#each Object.values(BLOCKS) as field}
|
{#each Object.values(BLOCKS) as field}
|
||||||
<Block secondary title={field.name} icon={field.icon} />
|
<Block secondary title={field.name} icon={field.icon} on:click={() => addNewField(field)} />
|
||||||
{/each}
|
{/each}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="block-row">
|
<div class="block-row">
|
||||||
<span class="block-row-title">Models</span>
|
<span class="block-row-title">Models</span>
|
||||||
<p>Blocks are pre-made fields and help you build your table quicker.</p>
|
<p>Blocks are pre-made fields and help you build your model quicker.</p>
|
||||||
<div class="blocks">
|
<div class="blocks">
|
||||||
{#each Object.values(MODELS) as model}
|
{#each Object.values(MODELS) as model}
|
||||||
<Block tertiary title={model.name} icon={model.icon} />
|
<Block tertiary title={model.name} icon={model.icon} />
|
||||||
|
@ -74,6 +75,7 @@
|
||||||
.block-row .blocks {
|
.block-row .blocks {
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-auto-flow: column;
|
grid-auto-flow: column;
|
||||||
|
grid-auto-columns: 110px;
|
||||||
grid-gap: 20px;
|
grid-gap: 20px;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
|
@ -18,7 +18,7 @@
|
||||||
|
|
||||||
div {
|
div {
|
||||||
padding: 0 10px 0 10px;
|
padding: 0 10px 0 10px;
|
||||||
width: 260px;
|
width: 90%;
|
||||||
height: 40px;
|
height: 40px;
|
||||||
border-radius: 3px;
|
border-radius: 3px;
|
||||||
display: flex;
|
display: flex;
|
||||||
|
@ -27,6 +27,8 @@
|
||||||
color: var(--ink);
|
color: var(--ink);
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
font-size: 16px;
|
font-size: 16px;
|
||||||
|
margin-top: 4px;
|
||||||
|
margin-bottom: 4px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.selected {
|
.selected {
|
||||||
|
|
|
@ -1,11 +1,10 @@
|
||||||
<script>
|
<script>
|
||||||
import { getContext } from "svelte"
|
import { getContext } from "svelte"
|
||||||
|
import { slide } from 'svelte/transition';
|
||||||
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 BlockNavigator from "./BlockNavigator.svelte"
|
||||||
import SchemaManagementDrawer from "../SchemaManagementDrawer.svelte"
|
|
||||||
import HierarchyRow from "../HierarchyRow.svelte"
|
|
||||||
import ListItem from "./ListItem.svelte"
|
import ListItem from "./ListItem.svelte"
|
||||||
import { Button } from "@budibase/bbui"
|
import { Button } from "@budibase/bbui"
|
||||||
|
|
||||||
|
@ -22,17 +21,17 @@
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
|
|
||||||
let selectedTab = "NAVIGATE"
|
$: selectedTab = $backendUiStore.tabs.NAVIGATION_PANEL
|
||||||
|
|
||||||
function selectModel(model) {
|
function selectModel(model, fieldName) {
|
||||||
backendUiStore.actions.models.select(model)
|
backendUiStore.actions.models.select(model)
|
||||||
}
|
|
||||||
|
|
||||||
function selectField(fieldName) {
|
if (fieldName) {
|
||||||
backendUiStore.update(state => {
|
backendUiStore.update(state => {
|
||||||
state.selectedField = fieldName
|
state.selectedField = fieldName
|
||||||
return state
|
return state
|
||||||
});
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function setupForNewModel() {
|
function setupForNewModel() {
|
||||||
|
@ -48,24 +47,28 @@
|
||||||
{#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 headings={HEADINGS} bind:value={selectedTab}>
|
<Switcher headings={HEADINGS} bind:value={$backendUiStore.tabs.NAVIGATION_PANEL}>
|
||||||
{#if selectedTab === 'NAVIGATE'}
|
{#if selectedTab === 'NAVIGATE'}
|
||||||
<Button secondary wide on:click={setupForNewModel}>Create New Model</Button>
|
<Button secondary wide on:click={setupForNewModel}>Create New Model</Button>
|
||||||
<div class="hierarchy-items-container">
|
<div class="hierarchy-items-container">
|
||||||
{#each $backendUiStore.models as model}
|
{#each $backendUiStore.models as model}
|
||||||
<ListItem
|
<ListItem
|
||||||
selected={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)} />
|
||||||
{#each Object.keys(model.schema) as field}
|
{#if model._id === $backendUiStore.selectedModel._id}
|
||||||
<ListItem
|
<div in:slide>
|
||||||
selected={field === $backendUiStore.selectedField}
|
{#each Object.keys(model.schema) as field}
|
||||||
indented
|
<ListItem
|
||||||
icon="ri-layout-column-fill"
|
selected={model._id === $backendUiStore.selectedModel._id && field === $backendUiStore.selectedField}
|
||||||
title={field}
|
indented
|
||||||
on:click={() => selectField(field)} />
|
icon="ri-layout-column-fill"
|
||||||
{/each}
|
title={field}
|
||||||
|
on:click={() => selectModel(model, field)} />
|
||||||
|
{/each}
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
{/each}
|
{/each}
|
||||||
</div>
|
</div>
|
||||||
{:else if selectedTab === 'ADD'}
|
{:else if selectedTab === 'ADD'}
|
||||||
|
|
|
@ -1,63 +0,0 @@
|
||||||
<script>
|
|
||||||
import { backendUiStore } from "builderStore"
|
|
||||||
import { Button } from "@budibase/bbui"
|
|
||||||
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 { keys, cloneDeep } from "lodash/fp"
|
|
||||||
|
|
||||||
const FIELD_TYPES = ["string", "number", "boolean", "link"]
|
|
||||||
|
|
||||||
let field = {}
|
|
||||||
|
|
||||||
$: field = $backendUiStore.draftModel.schema[$backendUiStore.selectedField] || {}
|
|
||||||
$: required =
|
|
||||||
field.constraints && field.constraints.presence && !constraints.presence.allowEmpty
|
|
||||||
|
|
||||||
const save = () => {
|
|
||||||
backendUiStore.actions.models.save({
|
|
||||||
instanceId: $backendUiStore.selectedDatabase._id,
|
|
||||||
model: $backendUiStore.draftModel
|
|
||||||
})
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<form on:submit|preventDefault class="uk-form-stacked">
|
|
||||||
<Textbox label="Name" bind:text={field.name} />
|
|
||||||
<Dropdown label="Type" bind:selected={field.type} options={FIELD_TYPES} />
|
|
||||||
|
|
||||||
<Checkbox label="Required" bind:checked={required} />
|
|
||||||
|
|
||||||
{#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'}
|
|
||||||
<select class="budibase__input" bind:value={field.modelId}>
|
|
||||||
<option value={''} />
|
|
||||||
{#each $backendUiStore.models as model}
|
|
||||||
<option value={model._id}>{model.name}</option>
|
|
||||||
{/each}
|
|
||||||
</select>
|
|
||||||
{/if}
|
|
||||||
</form>
|
|
||||||
<Button attention wide on:click={save}>Save</Button>
|
|
||||||
|
|
||||||
<style>
|
|
||||||
|
|
||||||
</style>
|
|
|
@ -0,0 +1,101 @@
|
||||||
|
<script>
|
||||||
|
import { backendUiStore } from "builderStore"
|
||||||
|
import { Button } from "@budibase/bbui"
|
||||||
|
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 { keys, cloneDeep } from "lodash/fp"
|
||||||
|
|
||||||
|
const FIELD_TYPES = ["string", "number", "boolean", "link"]
|
||||||
|
|
||||||
|
let field = {}
|
||||||
|
|
||||||
|
$: field =
|
||||||
|
$backendUiStore.draftModel.schema[$backendUiStore.selectedField] || {}
|
||||||
|
$: required =
|
||||||
|
field.constraints &&
|
||||||
|
field.constraints.presence &&
|
||||||
|
!constraints.presence.allowEmpty
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div class="info">
|
||||||
|
<div class="field-box">
|
||||||
|
<header>Name</header>
|
||||||
|
<input class="budibase__input" type="text" bind:value={field.name} />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="info">
|
||||||
|
<div class="field-box">
|
||||||
|
<header>Type</header>
|
||||||
|
<span>{field.type}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="info">
|
||||||
|
<div class="required-field">
|
||||||
|
<label>Required</label>
|
||||||
|
<input type="checkbox" />
|
||||||
|
</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'}
|
||||||
|
<select class="budibase__input" bind:value={field.modelId}>
|
||||||
|
<option value={''} />
|
||||||
|
{#each $backendUiStore.models as model}
|
||||||
|
<option value={model._id}>{model.name}</option>
|
||||||
|
{/each}
|
||||||
|
</select>
|
||||||
|
{/if}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.info {
|
||||||
|
background: var(--light-grey);
|
||||||
|
padding: 12px;
|
||||||
|
margin-bottom: 5px;
|
||||||
|
border-radius: 3px;
|
||||||
|
}
|
||||||
|
|
||||||
|
label {
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.required-field {
|
||||||
|
display: grid;
|
||||||
|
align-items: center;
|
||||||
|
grid-template-columns: 40% 1fr;
|
||||||
|
}
|
||||||
|
|
||||||
|
.field-box header {
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: 500;
|
||||||
|
margin-bottom: 16px;
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -4,7 +4,7 @@
|
||||||
import { notifier } from "@beyonk/svelte-notifications"
|
import { notifier } from "@beyonk/svelte-notifications"
|
||||||
import { store, backendUiStore } from "builderStore"
|
import { store, backendUiStore } from "builderStore"
|
||||||
import api from "builderStore/api"
|
import api from "builderStore/api"
|
||||||
import FieldView from "./FieldView.svelte";
|
import ModelFieldEditor from "./ModelFieldEditor.svelte"
|
||||||
|
|
||||||
const { open, close } = getContext("simple-modal")
|
const { open, close } = getContext("simple-modal")
|
||||||
|
|
||||||
|
@ -13,52 +13,57 @@
|
||||||
title: "Setup",
|
title: "Setup",
|
||||||
key: "SETUP",
|
key: "SETUP",
|
||||||
},
|
},
|
||||||
{
|
|
||||||
title: "Filter",
|
|
||||||
key: "FILTER",
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
title: "Delete",
|
title: "Delete",
|
||||||
key: "DELETE",
|
key: "DELETE",
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
|
|
||||||
let selectedTab = "SETUP"
|
$: selectedTab = $backendUiStore.tabs.SETUP_PANEL
|
||||||
|
|
||||||
$: edited = $backendUiStore.draftModel.name !== $backendUiStore.selectedModel.name
|
$: edited =
|
||||||
|
$backendUiStore.draftModel.name !== $backendUiStore.selectedModel.name
|
||||||
|
|
||||||
async function deleteModel() {
|
async function deleteModel() {
|
||||||
const modelToDelete = $backendUiStore.selectedModel
|
const model = $backendUiStore.selectedModel
|
||||||
if ($backendUiStore.selectedField) {
|
const instanceId = $backendUiStore.selectedDatabase._id
|
||||||
delete modelToDelete[$backendUiStore.selectedField]
|
const field = $backendUiStore.selectedField
|
||||||
|
|
||||||
|
if (field) {
|
||||||
|
delete model.schema[field]
|
||||||
|
backendUiStore.actions.models.save({ model, instanceId });
|
||||||
|
notifier.danger(`Field ${field} deleted.`);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const DELETE_MODEL_URL = `/api/${$backendUiStore.selectedDatabase._id}/models/${modelToDelete._id}/${modelToDelete._rev}`
|
const DELETE_MODEL_URL = `/api/${instanceId}/models/${model._id}/${model._rev}`
|
||||||
const response = await api.delete(DELETE_MODEL_URL)
|
const response = await api.delete(DELETE_MODEL_URL)
|
||||||
backendUiStore.update(state => {
|
backendUiStore.update(state => {
|
||||||
state.selectedView = null
|
state.selectedView = null
|
||||||
state.models = state.models.filter(
|
state.models = state.models.filter(
|
||||||
model => model._id !== modelToDelete._id
|
({ _id }) => _id !== model._id
|
||||||
)
|
)
|
||||||
notifier.danger(`${modelToDelete.name} deleted successfully.`)
|
notifier.danger(`${model.name} deleted successfully.`)
|
||||||
return state
|
return state
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
async function saveModel() {
|
async function saveModel() {
|
||||||
await backendUiStore.actions.models.save({
|
await backendUiStore.actions.models.save({
|
||||||
instanceId: $backendUiStore.selectedDatabase._id,
|
instanceId: $backendUiStore.selectedDatabase._id,
|
||||||
model: $backendUiStore.draftModel
|
model: $backendUiStore.draftModel,
|
||||||
})
|
})
|
||||||
notifier.success("Success! Your changes have been saved. Please continue on with your greatness.");
|
notifier.success(
|
||||||
|
"Success! Your changes have been saved. Please continue on with your greatness."
|
||||||
|
)
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="items-root">
|
<div class="items-root">
|
||||||
<Switcher headings={ITEMS} bind:value={selectedTab}>
|
<Switcher headings={ITEMS} bind:value={$backendUiStore.tabs.SETUP_PANEL}>
|
||||||
{#if selectedTab === 'SETUP'}
|
{#if selectedTab === 'SETUP'}
|
||||||
{#if $backendUiStore.selectedField}
|
{#if $backendUiStore.selectedField}
|
||||||
<FieldView />
|
<ModelFieldEditor />
|
||||||
{:else}
|
{:else}
|
||||||
<div class="titled-input">
|
<div class="titled-input">
|
||||||
<header>Name</header>
|
<header>Name</header>
|
||||||
|
@ -71,13 +76,10 @@
|
||||||
<header>Import Data</header>
|
<header>Import Data</header>
|
||||||
<Button wide secondary>Import CSV</Button>
|
<Button wide secondary>Import CSV</Button>
|
||||||
</div>
|
</div>
|
||||||
<Button
|
|
||||||
attention
|
|
||||||
wide
|
|
||||||
on:click={saveModel}>
|
|
||||||
Save
|
|
||||||
</Button>
|
|
||||||
{/if}
|
{/if}
|
||||||
|
<footer>
|
||||||
|
<Button attention wide on:click={saveModel}>Save</Button>
|
||||||
|
</footer>
|
||||||
{:else if selectedTab === 'DELETE'}
|
{:else if selectedTab === 'DELETE'}
|
||||||
<div class="titled-input">
|
<div class="titled-input">
|
||||||
<header>Danger Zone</header>
|
<header>Danger Zone</header>
|
||||||
|
@ -92,6 +94,10 @@
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
footer {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
.items-root {
|
.items-root {
|
||||||
padding: 20px;
|
padding: 20px;
|
||||||
display: flex;
|
display: flex;
|
||||||
|
@ -102,9 +108,8 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
.titled-input {
|
.titled-input {
|
||||||
padding: 20px;
|
padding: 12px;
|
||||||
background: var(--light-grey);
|
background: var(--light-grey);
|
||||||
margin-top: 20px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.titled-input header {
|
.titled-input header {
|
||||||
|
|
|
@ -1,143 +0,0 @@
|
||||||
<script>
|
|
||||||
import { getContext, onMount } from "svelte"
|
|
||||||
import { store, backendUiStore } from "builderStore"
|
|
||||||
import HierarchyRow from "./HierarchyRow.svelte"
|
|
||||||
import NavItem from "./NavItem.svelte"
|
|
||||||
import getIcon from "components/common/icon"
|
|
||||||
import api from "builderStore/api"
|
|
||||||
import {
|
|
||||||
CreateEditModelModal,
|
|
||||||
CreateEditViewModal,
|
|
||||||
} from "components/database/ModelDataTable/modals"
|
|
||||||
|
|
||||||
const { open, close } = getContext("simple-modal")
|
|
||||||
|
|
||||||
function editModel() {
|
|
||||||
open(
|
|
||||||
CreateEditModelModal,
|
|
||||||
{
|
|
||||||
model: node,
|
|
||||||
onClosed: close,
|
|
||||||
},
|
|
||||||
{ styleContent: { padding: "0" } }
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
function newModel() {
|
|
||||||
open(
|
|
||||||
CreateEditModelModal,
|
|
||||||
{
|
|
||||||
onClosed: close,
|
|
||||||
},
|
|
||||||
{ styleContent: { padding: "0" } }
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
function newView() {
|
|
||||||
open(
|
|
||||||
CreateEditViewModal,
|
|
||||||
{
|
|
||||||
onClosed: close,
|
|
||||||
},
|
|
||||||
{ styleContent: { padding: "0" } }
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
function selectModel(model) {
|
|
||||||
backendUiStore.update(state => {
|
|
||||||
state.selectedModel = model
|
|
||||||
state.selectedView = `all_${model._id}`
|
|
||||||
return state
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
async function deleteModel(modelToDelete) {
|
|
||||||
const DELETE_MODEL_URL = `/api/${instanceId}/models/${node._id}/${node._rev}`
|
|
||||||
const response = await api.delete(DELETE_MODEL_URL)
|
|
||||||
backendUiStore.update(state => {
|
|
||||||
state.models = state.models.filter(
|
|
||||||
model => model._id !== modelToDelete._id
|
|
||||||
)
|
|
||||||
state.selectedView = {}
|
|
||||||
return state
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
function selectView(view) {
|
|
||||||
backendUiStore.update(state => {
|
|
||||||
state.selectedView = view.name
|
|
||||||
return state
|
|
||||||
})
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<div class="items-root">
|
|
||||||
<div class="hierarchy">
|
|
||||||
<div class="components-list-container">
|
|
||||||
<div class="nav-group-header">
|
|
||||||
<div class="hierarchy-title">Models</div>
|
|
||||||
<div class="uk-inline">
|
|
||||||
<i class="ri-add-line hoverable" on:click={newModel} />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="hierarchy-items-container">
|
|
||||||
{#each $backendUiStore.models as model}
|
|
||||||
<HierarchyRow onSelect={selectModel} node={model} type="model" />
|
|
||||||
{/each}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="hierarchy">
|
|
||||||
<div class="components-list-container">
|
|
||||||
<div class="nav-group-header">
|
|
||||||
<div class="hierarchy-title">Views</div>
|
|
||||||
<div class="uk-inline">
|
|
||||||
<i class="ri-add-line hoverable" on:click={newView} />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="hierarchy-items-container">
|
|
||||||
{#each $backendUiStore.views as view}
|
|
||||||
<HierarchyRow onSelect={selectView} node={view} type="view" />
|
|
||||||
{/each}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<style>
|
|
||||||
.items-root {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
max-height: 100%;
|
|
||||||
height: 100%;
|
|
||||||
background-color: var(--white);
|
|
||||||
}
|
|
||||||
|
|
||||||
.nav-group-header {
|
|
||||||
display: flex;
|
|
||||||
justify-content: space-between;
|
|
||||||
align-items: center;
|
|
||||||
padding: 20px 20px 10px 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.hierarchy-title {
|
|
||||||
align-items: center;
|
|
||||||
font-size: 18px;
|
|
||||||
font-weight: 700;
|
|
||||||
text-rendering: optimizeLegibility;
|
|
||||||
color: var(--ink);
|
|
||||||
}
|
|
||||||
|
|
||||||
.hierarchy {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
}
|
|
||||||
|
|
||||||
.hierarchy-items-container {
|
|
||||||
flex: 1 1 auto;
|
|
||||||
overflow-y: auto;
|
|
||||||
}
|
|
||||||
</style>
|
|
|
@ -50,9 +50,9 @@ export const FIELDS = {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
IMAGE: {
|
IMAGE: {
|
||||||
name: "Image",
|
name: "File",
|
||||||
icon: "ri-image-line",
|
icon: "ri-image-line",
|
||||||
type: "image",
|
type: "file",
|
||||||
constraints: {
|
constraints: {
|
||||||
type: "string",
|
type: "string",
|
||||||
presence: false,
|
presence: false,
|
||||||
|
|
|
@ -1,647 +0,0 @@
|
||||||
body, html {
|
|
||||||
margin: 0px;
|
|
||||||
padding: 0px;
|
|
||||||
overflow: hidden;
|
|
||||||
background-repeat: repeat;
|
|
||||||
background-size: 30px 30px;
|
|
||||||
background-color: #FBFBFB;
|
|
||||||
height: 100%;
|
|
||||||
}
|
|
||||||
#navigation {
|
|
||||||
height: 71px;
|
|
||||||
background-color: #FFF;
|
|
||||||
border: 1px solid #E8E8EF;
|
|
||||||
width: 100%;
|
|
||||||
display: table;
|
|
||||||
box-sizing: border-box;
|
|
||||||
position: fixed;
|
|
||||||
top: 0;
|
|
||||||
z-index: 9
|
|
||||||
}
|
|
||||||
#back {
|
|
||||||
width: 40px;
|
|
||||||
height: 40px;
|
|
||||||
border-radius: 100px;
|
|
||||||
background-color: #F1F4FC;
|
|
||||||
text-align: center;
|
|
||||||
display: inline-block;
|
|
||||||
vertical-align: top;
|
|
||||||
margin-top: 12px;
|
|
||||||
margin-right: 10px
|
|
||||||
}
|
|
||||||
#back img {
|
|
||||||
margin-top: 13px;
|
|
||||||
}
|
|
||||||
#names {
|
|
||||||
display: inline-block;
|
|
||||||
vertical-align: top;
|
|
||||||
}
|
|
||||||
#title {
|
|
||||||
font-family: Roboto;
|
|
||||||
font-weight: 500;
|
|
||||||
font-size: 16px;
|
|
||||||
color: #393C44;
|
|
||||||
margin-bottom: 0px;
|
|
||||||
}
|
|
||||||
#subtitle {
|
|
||||||
font-family: Roboto;
|
|
||||||
color: #808292;
|
|
||||||
font-size: 14px;
|
|
||||||
margin-top: 5px;
|
|
||||||
}
|
|
||||||
#leftside {
|
|
||||||
display: inline-block;
|
|
||||||
vertical-align: middle;
|
|
||||||
margin-left: 20px;
|
|
||||||
}
|
|
||||||
#centerswitch {
|
|
||||||
position: absolute;
|
|
||||||
width: 222px;
|
|
||||||
left: 50%;
|
|
||||||
margin-left: -111px;
|
|
||||||
top: 15px;
|
|
||||||
}
|
|
||||||
#leftswitch {
|
|
||||||
border: 1px solid #E8E8EF;
|
|
||||||
background-color: #FBFBFB;
|
|
||||||
width: 111px;
|
|
||||||
height: 39px;
|
|
||||||
line-height: 39px;
|
|
||||||
border-radius: 5px 0px 0px 5px;
|
|
||||||
font-family: Roboto;
|
|
||||||
color: #393C44;
|
|
||||||
display: inline-block;
|
|
||||||
font-size: 14px;
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
#rightswitch {
|
|
||||||
font-family: Roboto;
|
|
||||||
color: #808292;
|
|
||||||
border-radius: 0px 5px 5px 0px;
|
|
||||||
border: 1px solid #E8E8EF;
|
|
||||||
height: 39px;
|
|
||||||
width: 102px;
|
|
||||||
display: inline-block;
|
|
||||||
font-size: 14px;
|
|
||||||
line-height: 39px;
|
|
||||||
text-align: center;
|
|
||||||
margin-left: -5px;
|
|
||||||
}
|
|
||||||
#discard {
|
|
||||||
font-family: Roboto;
|
|
||||||
font-weight: 500;
|
|
||||||
font-size: 14px;
|
|
||||||
color: #A6A6B3;
|
|
||||||
width: 95px;
|
|
||||||
height: 38px;
|
|
||||||
border: 1px solid #E8E8EF;
|
|
||||||
border-radius: 5px;
|
|
||||||
text-align: center;
|
|
||||||
line-height: 38px;
|
|
||||||
display: inline-block;
|
|
||||||
vertical-align: top;
|
|
||||||
transition: all .2s cubic-bezier(.05,.03,.35,1);
|
|
||||||
}
|
|
||||||
#discard:hover {
|
|
||||||
cursor: pointer;
|
|
||||||
opacity: .7;
|
|
||||||
}
|
|
||||||
#publish {
|
|
||||||
font-family: Roboto;
|
|
||||||
font-weight: 500;
|
|
||||||
font-size: 14px;
|
|
||||||
color: #FFF;
|
|
||||||
background-color: #217CE8;
|
|
||||||
border-radius: 5px;
|
|
||||||
width: 143px;
|
|
||||||
height: 38px;
|
|
||||||
margin-left: 10px;
|
|
||||||
display: inline-block;
|
|
||||||
vertical-align: top;
|
|
||||||
text-align: center;
|
|
||||||
line-height: 38px;
|
|
||||||
margin-right: 20px;
|
|
||||||
transition: all .2s cubic-bezier(.05,.03,.35,1);
|
|
||||||
}
|
|
||||||
#publish:hover {
|
|
||||||
cursor: pointer;
|
|
||||||
opacity: .7;
|
|
||||||
}
|
|
||||||
#buttonsright {
|
|
||||||
float: right;
|
|
||||||
margin-top: 15px;
|
|
||||||
}
|
|
||||||
#leftcard {
|
|
||||||
width: 363px;
|
|
||||||
background-color: #FFF;
|
|
||||||
border: 1px solid #E8E8EF;
|
|
||||||
box-sizing: border-box;
|
|
||||||
padding-top: 85px;
|
|
||||||
padding-left: 20px;
|
|
||||||
height: 100%;
|
|
||||||
position: absolute;
|
|
||||||
z-index: 2;
|
|
||||||
}
|
|
||||||
#search input {
|
|
||||||
width: 318px;
|
|
||||||
height: 40px;
|
|
||||||
background-color: #FFF;
|
|
||||||
border: 1px solid #E8E8EF;
|
|
||||||
box-sizing: border-box;
|
|
||||||
box-shadow: 0px 2px 8px rgba(34,34,87,0.05);
|
|
||||||
border-radius: 5px;
|
|
||||||
text-indent: 35px;
|
|
||||||
font-family: Roboto;
|
|
||||||
font-size: 16px;
|
|
||||||
}
|
|
||||||
::-webkit-input-placeholder { /* Edge */
|
|
||||||
color: #C9C9D5;
|
|
||||||
}
|
|
||||||
|
|
||||||
:-ms-input-placeholder { /* Internet Explorer 10-11 */
|
|
||||||
color: #C9C9D5
|
|
||||||
}
|
|
||||||
|
|
||||||
::placeholder {
|
|
||||||
color: #C9C9D5;
|
|
||||||
}
|
|
||||||
#search img {
|
|
||||||
position: absolute;
|
|
||||||
margin-top: 10px;
|
|
||||||
width: 18px;
|
|
||||||
margin-left: 12px;
|
|
||||||
}
|
|
||||||
#header {
|
|
||||||
font-size: 20px;
|
|
||||||
font-family: Roboto;
|
|
||||||
font-weight: bold;
|
|
||||||
color: #393C44;
|
|
||||||
}
|
|
||||||
#subnav {
|
|
||||||
border-bottom: 1px solid #E8E8EF;
|
|
||||||
width: calc(100% + 20px);
|
|
||||||
margin-left: -20px;
|
|
||||||
margin-top: 10px;
|
|
||||||
}
|
|
||||||
.navdisabled {
|
|
||||||
transition: all .3s cubic-bezier(.05,.03,.35,1);
|
|
||||||
}
|
|
||||||
.navdisabled:hover {
|
|
||||||
cursor: pointer;
|
|
||||||
opacity: .5;
|
|
||||||
}
|
|
||||||
.navactive {
|
|
||||||
color: #393C44!important;
|
|
||||||
}
|
|
||||||
#triggers {
|
|
||||||
margin-left: 20px;
|
|
||||||
font-family: Roboto;
|
|
||||||
font-weight: 500;
|
|
||||||
font-size: 14px;
|
|
||||||
text-align: center;
|
|
||||||
color: #808292;
|
|
||||||
width: calc(88% / 3);
|
|
||||||
height: 48px;
|
|
||||||
line-height: 48px;
|
|
||||||
display: inline-block;
|
|
||||||
float: left;
|
|
||||||
}
|
|
||||||
.navactive:after {
|
|
||||||
display: block;
|
|
||||||
content: "";
|
|
||||||
width: 100%;
|
|
||||||
height: 4px;
|
|
||||||
background-color: #217CE8;
|
|
||||||
margin-top: -4px;
|
|
||||||
}
|
|
||||||
#actions {
|
|
||||||
display: inline-block;
|
|
||||||
font-family: Roboto;
|
|
||||||
font-weight: 500;
|
|
||||||
color: #808292;
|
|
||||||
font-size: 14px;
|
|
||||||
height: 48px;
|
|
||||||
line-height: 48px;
|
|
||||||
width: calc(88% / 3);
|
|
||||||
text-align: center;
|
|
||||||
float: left;
|
|
||||||
}
|
|
||||||
#loggers {
|
|
||||||
width: calc(88% / 3);
|
|
||||||
display: inline-block;
|
|
||||||
font-family: Roboto;
|
|
||||||
font-weight: 500;
|
|
||||||
color: #808292;
|
|
||||||
font-size: 14px;
|
|
||||||
height: 48px;
|
|
||||||
line-height: 48px;
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
#footer {
|
|
||||||
position: absolute;
|
|
||||||
left: 0;
|
|
||||||
padding-left: 20px;
|
|
||||||
line-height: 40px;
|
|
||||||
bottom: 0;
|
|
||||||
width: 362px;
|
|
||||||
border: 1px solid #E8E8EF;
|
|
||||||
height: 67px;
|
|
||||||
box-sizing: border-box;
|
|
||||||
background-color: #FFF;
|
|
||||||
font-family: Roboto;
|
|
||||||
font-size: 14px;
|
|
||||||
}
|
|
||||||
#footer a {
|
|
||||||
text-decoration: none;
|
|
||||||
color: #393C44;
|
|
||||||
transition: all .2s cubic-bezier(.05,.03,.35,1);
|
|
||||||
}
|
|
||||||
#footer a:hover {
|
|
||||||
opacity: .5;
|
|
||||||
}
|
|
||||||
#footer span {
|
|
||||||
color: #808292;
|
|
||||||
}
|
|
||||||
#footer p {
|
|
||||||
display: inline-block;
|
|
||||||
color: #808292;
|
|
||||||
}
|
|
||||||
#footer img {
|
|
||||||
margin-left: 5px;
|
|
||||||
margin-right: 5px;
|
|
||||||
}
|
|
||||||
.blockelem:first-child {
|
|
||||||
margin-top: 20px
|
|
||||||
}
|
|
||||||
.blockelem {
|
|
||||||
padding-top: 10px;
|
|
||||||
width: 318px;
|
|
||||||
border: 1px solid transparent;
|
|
||||||
transition-property: box-shadow, height;
|
|
||||||
transition-duration: .2s;
|
|
||||||
transition-timing-function: cubic-bezier(.05,.03,.35,1);
|
|
||||||
border-radius: 5px;
|
|
||||||
box-shadow: 0px 0px 30px rgba(22, 33, 74, 0);
|
|
||||||
box-sizing: border-box;
|
|
||||||
}
|
|
||||||
.blockelem:hover {
|
|
||||||
box-shadow: 0px 4px 30px rgba(22, 33, 74, 0.08);
|
|
||||||
border-radius: 5px;
|
|
||||||
background-color: #FFF;
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
.grabme, .blockico {
|
|
||||||
display: inline-block;
|
|
||||||
}
|
|
||||||
|
|
||||||
.grabme {
|
|
||||||
margin-top: 10px;
|
|
||||||
margin-left: 10px;
|
|
||||||
margin-bottom: -14px;
|
|
||||||
width: 15px;
|
|
||||||
}
|
|
||||||
#blocklist {
|
|
||||||
height: calc(100% - 220px);
|
|
||||||
overflow: auto;
|
|
||||||
}
|
|
||||||
#proplist {
|
|
||||||
height: calc(100% - 305px);
|
|
||||||
overflow: auto;
|
|
||||||
margin-top: -30px;
|
|
||||||
padding-top: 30px;
|
|
||||||
}
|
|
||||||
.blockin {
|
|
||||||
display: inline-block;
|
|
||||||
vertical-align: top;
|
|
||||||
margin-left: 12px;
|
|
||||||
}
|
|
||||||
.blockico {
|
|
||||||
width: 36px;
|
|
||||||
height: 36px;
|
|
||||||
background-color: #F1F4FC;
|
|
||||||
border-radius: 5px;
|
|
||||||
text-align: center;
|
|
||||||
white-space: nowrap;
|
|
||||||
}
|
|
||||||
|
|
||||||
.blockico i {
|
|
||||||
font-size: 24px;
|
|
||||||
color: var(--dark-grey);
|
|
||||||
}
|
|
||||||
|
|
||||||
.blockico span {
|
|
||||||
height: 100%;
|
|
||||||
width: 0px;
|
|
||||||
display: inline-block;
|
|
||||||
vertical-align: middle;
|
|
||||||
}
|
|
||||||
.blockico img {
|
|
||||||
vertical-align: middle;
|
|
||||||
margin-left: auto;
|
|
||||||
margin-right: auto;
|
|
||||||
display: inline-block;
|
|
||||||
}
|
|
||||||
.blocktext {
|
|
||||||
display: inline-block;
|
|
||||||
width: 220px;
|
|
||||||
vertical-align: top;
|
|
||||||
margin-left: 12px
|
|
||||||
}
|
|
||||||
.blocktitle {
|
|
||||||
margin: 0px!important;
|
|
||||||
padding: 0px!important;
|
|
||||||
font-family: Roboto;
|
|
||||||
font-weight: 500;
|
|
||||||
font-size: 16px;
|
|
||||||
color: #393C44;
|
|
||||||
}
|
|
||||||
.blockdesc {
|
|
||||||
margin-top: 5px;
|
|
||||||
font-family: Roboto;
|
|
||||||
color: #808292;
|
|
||||||
font-size: 14px;
|
|
||||||
line-height: 21px;
|
|
||||||
}
|
|
||||||
.blockdisabled {
|
|
||||||
background-color: #F0F2F9;
|
|
||||||
opacity: .5;
|
|
||||||
}
|
|
||||||
#closecard {
|
|
||||||
position: absolute;
|
|
||||||
margin-left: 340px;
|
|
||||||
background-color: #FFF;
|
|
||||||
border-radius: 0px 5px 5px 0px;
|
|
||||||
border-bottom: 1px solid #E8E8EF;
|
|
||||||
border-right: 1px solid #E8E8EF;
|
|
||||||
border-top: 1px solid #E8E8EF;
|
|
||||||
width: 53px;
|
|
||||||
height: 53px;
|
|
||||||
text-align: center;
|
|
||||||
z-index: 10;
|
|
||||||
}
|
|
||||||
#closecard img {
|
|
||||||
margin-top: 15px
|
|
||||||
}
|
|
||||||
#canvas {
|
|
||||||
border: 1px solid green;
|
|
||||||
position: absolute;
|
|
||||||
width: calc(100% - 361px);
|
|
||||||
height: calc(100% - 71px);
|
|
||||||
top: 71px;
|
|
||||||
left: 361px;
|
|
||||||
z-index: 0;
|
|
||||||
overflow: auto;
|
|
||||||
}
|
|
||||||
#propwrap {
|
|
||||||
position: absolute;
|
|
||||||
right: 0;
|
|
||||||
top: 0;
|
|
||||||
width: 311px;
|
|
||||||
height: 100%;
|
|
||||||
padding-left: 20px;
|
|
||||||
overflow: hidden;
|
|
||||||
z-index: -2;
|
|
||||||
}
|
|
||||||
#properties {
|
|
||||||
position: absolute;
|
|
||||||
height: 100%;
|
|
||||||
width: 311px;
|
|
||||||
background-color: #FFF;
|
|
||||||
right: -150px;
|
|
||||||
opacity: 0;
|
|
||||||
z-index: 2;
|
|
||||||
top: 0px;
|
|
||||||
box-shadow: -4px 0px 40px rgba(26, 26, 73, 0);
|
|
||||||
padding-left: 20px;
|
|
||||||
transition: all .25s cubic-bezier(.05,.03,.35,1);
|
|
||||||
}
|
|
||||||
.itson {
|
|
||||||
z-index: 2!important;
|
|
||||||
}
|
|
||||||
.expanded {
|
|
||||||
right: 0!important;
|
|
||||||
opacity: 1!important;
|
|
||||||
box-shadow: -4px 0px 40px rgba(26, 26, 73, 0.05);
|
|
||||||
z-index: 2;
|
|
||||||
}
|
|
||||||
#header2 {
|
|
||||||
font-size: 20px;
|
|
||||||
font-family: Roboto;
|
|
||||||
font-weight: bold;
|
|
||||||
color: #393C44;
|
|
||||||
margin-top: 101px;
|
|
||||||
}
|
|
||||||
#close {
|
|
||||||
margin-top: 100px;
|
|
||||||
position: absolute;
|
|
||||||
right: 20px;
|
|
||||||
z-index: 9999;
|
|
||||||
transition: all .25s cubic-bezier(.05,.03,.35,1);
|
|
||||||
}
|
|
||||||
#close:hover {
|
|
||||||
cursor: pointer;
|
|
||||||
opacity: .7;
|
|
||||||
}
|
|
||||||
#propswitch {
|
|
||||||
border-bottom: 1px solid #E8E8EF;
|
|
||||||
width: 331px;
|
|
||||||
margin-top: 10px;
|
|
||||||
margin-left: -20px;
|
|
||||||
margin-bottom: 30px;
|
|
||||||
}
|
|
||||||
#dataprop {
|
|
||||||
font-family: Roboto;
|
|
||||||
font-weight: 500;
|
|
||||||
font-size: 14px;
|
|
||||||
text-align: center;
|
|
||||||
color: #393C44;
|
|
||||||
width: calc(88% / 3);
|
|
||||||
height: 48px;
|
|
||||||
line-height: 48px;
|
|
||||||
display: inline-block;
|
|
||||||
float: left;
|
|
||||||
margin-left: 20px;
|
|
||||||
}
|
|
||||||
#dataprop:after {
|
|
||||||
display: block;
|
|
||||||
content: "";
|
|
||||||
width: 100%;
|
|
||||||
height: 4px;
|
|
||||||
background-color: #217CE8;
|
|
||||||
margin-top: -4px;
|
|
||||||
}
|
|
||||||
#alertprop {
|
|
||||||
display: inline-block;
|
|
||||||
font-family: Roboto;
|
|
||||||
font-weight: 500;
|
|
||||||
color: #808292;
|
|
||||||
font-size: 14px;
|
|
||||||
height: 48px;
|
|
||||||
line-height: 48px;
|
|
||||||
width: calc(88% / 3);
|
|
||||||
text-align: center;
|
|
||||||
float: left;
|
|
||||||
}
|
|
||||||
#logsprop {
|
|
||||||
width: calc(88% / 3);
|
|
||||||
display: inline-block;
|
|
||||||
font-family: Roboto;
|
|
||||||
font-weight: 500;
|
|
||||||
color: #808292;
|
|
||||||
font-size: 14px;
|
|
||||||
height: 48px;
|
|
||||||
line-height: 48px;
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
.inputlabel {
|
|
||||||
font-family: Roboto;
|
|
||||||
font-size: 14px;
|
|
||||||
color: #253134;
|
|
||||||
}
|
|
||||||
.dropme {
|
|
||||||
background-color: #FFF;
|
|
||||||
border-radius: 5px;
|
|
||||||
border: 1px solid #E8E8EF;
|
|
||||||
box-shadow: 0px 2px 8px rgba(34, 34, 87, 0.05);
|
|
||||||
font-family: Roboto;
|
|
||||||
font-size: 14px;
|
|
||||||
color: #253134;
|
|
||||||
text-indent: 20px;
|
|
||||||
height: 40px;
|
|
||||||
line-height: 40px;
|
|
||||||
width: 287px;
|
|
||||||
margin-bottom: 25px;
|
|
||||||
}
|
|
||||||
.dropme img {
|
|
||||||
margin-top: 17px;
|
|
||||||
float: right;
|
|
||||||
margin-right: 15px;
|
|
||||||
}
|
|
||||||
.checkus {
|
|
||||||
margin-bottom: 10px;
|
|
||||||
}
|
|
||||||
.checkus img {
|
|
||||||
display: inline-block;
|
|
||||||
vertical-align: middle;
|
|
||||||
}
|
|
||||||
.checkus p {
|
|
||||||
display: inline-block;
|
|
||||||
font-family: Roboto;
|
|
||||||
font-size: 14px;
|
|
||||||
vertical-align: middle;
|
|
||||||
margin-left: 10px;
|
|
||||||
}
|
|
||||||
#divisionthing {
|
|
||||||
height: 1px;
|
|
||||||
width: 100%;
|
|
||||||
background-color: #E8E8EF;
|
|
||||||
position: absolute;
|
|
||||||
right: 0px;
|
|
||||||
bottom: 80;
|
|
||||||
}
|
|
||||||
#removeblock {
|
|
||||||
border-radius: 5px;
|
|
||||||
position: absolute;
|
|
||||||
bottom: 20px;
|
|
||||||
font-family: Roboto;
|
|
||||||
font-size: 14px;
|
|
||||||
text-align: center;
|
|
||||||
width: 287px;
|
|
||||||
height: 38px;
|
|
||||||
line-height: 38px;
|
|
||||||
color: #253134;
|
|
||||||
border: 1px solid #E8E8EF;
|
|
||||||
transition: all .3s cubic-bezier(.05,.03,.35,1);
|
|
||||||
}
|
|
||||||
#removeblock:hover {
|
|
||||||
cursor: pointer;
|
|
||||||
opacity: .5;
|
|
||||||
}
|
|
||||||
.noselect {
|
|
||||||
-webkit-touch-callout: none; /* iOS Safari */
|
|
||||||
-webkit-user-select: none; /* Safari */
|
|
||||||
-khtml-user-select: none; /* Konqueror HTML */
|
|
||||||
-moz-user-select: none; /* Old versions of Firefox */
|
|
||||||
-ms-user-select: none; /* Internet Explorer/Edge */
|
|
||||||
user-select: none; /* Non-prefixed version, currently
|
|
||||||
supported by Chrome, Opera and Firefox */
|
|
||||||
}
|
|
||||||
.blockyname {
|
|
||||||
font-family: Roboto;
|
|
||||||
font-weight: 500;
|
|
||||||
color: #253134;
|
|
||||||
display: inline-block;
|
|
||||||
vertical-align: middle;
|
|
||||||
margin-left: 8px;
|
|
||||||
font-size: 16px;
|
|
||||||
}
|
|
||||||
.blockyleft img {
|
|
||||||
display: inline-block;
|
|
||||||
vertical-align: middle;
|
|
||||||
}
|
|
||||||
.blockyright {
|
|
||||||
display: inline-block;
|
|
||||||
float: right;
|
|
||||||
vertical-align: middle;
|
|
||||||
margin-right: 20px;
|
|
||||||
margin-top: 10px;
|
|
||||||
width: 28px;
|
|
||||||
height: 28px;
|
|
||||||
border-radius: 5px;
|
|
||||||
text-align: center;
|
|
||||||
background-color: #FFF;
|
|
||||||
transition: all .3s cubic-bezier(.05,.03,.35,1);
|
|
||||||
z-index: 10;
|
|
||||||
}
|
|
||||||
.blockyright:hover {
|
|
||||||
background-color: #F1F4FC;
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
.blockyright img {
|
|
||||||
margin-top: 12px;
|
|
||||||
}
|
|
||||||
.blockyleft {
|
|
||||||
display: inline-block;
|
|
||||||
margin-left: 20px;
|
|
||||||
}
|
|
||||||
.blockydiv {
|
|
||||||
width: 100%;
|
|
||||||
height: 1px;
|
|
||||||
background-color: #E9E9EF;
|
|
||||||
}
|
|
||||||
.blockyinfo {
|
|
||||||
font-family: Roboto;
|
|
||||||
font-size: 14px;
|
|
||||||
color: #808292;
|
|
||||||
margin-top: 15px;
|
|
||||||
text-indent: 20px;
|
|
||||||
margin-bottom: 20px;
|
|
||||||
}
|
|
||||||
.blockyinfo span {
|
|
||||||
color: #253134;
|
|
||||||
font-weight: 500;
|
|
||||||
display: inline-block;
|
|
||||||
border-bottom: 1px solid #D3DCEA;
|
|
||||||
line-height: 20px;
|
|
||||||
text-indent: 0px;
|
|
||||||
}
|
|
||||||
.block {
|
|
||||||
background-color: #FFF;
|
|
||||||
margin-top: 0px!important;
|
|
||||||
box-shadow: 0px 4px 30px rgba(22, 33, 74, 0.05);
|
|
||||||
}
|
|
||||||
.selectedblock {
|
|
||||||
border: 2px solid #217CE8;
|
|
||||||
box-shadow: 0px 4px 30px rgba(22, 33, 74, 0.08);
|
|
||||||
}
|
|
||||||
|
|
||||||
@media only screen and (max-width: 832px) {
|
|
||||||
#centerswitch {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@media only screen and (max-width: 560px) {
|
|
||||||
#names {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -0,0 +1 @@
|
||||||
|
<slot />
|
|
@ -1,6 +1,7 @@
|
||||||
<script>
|
<script>
|
||||||
import { getContext } from "svelte"
|
import { getContext } from "svelte"
|
||||||
import NewModel from "./NewModel.svelte"
|
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 { store, backendUiStore } from "builderStore"
|
import { store, backendUiStore } from "builderStore"
|
||||||
import ActionButton from "components/common/ActionButton.svelte"
|
import ActionButton from "components/common/ActionButton.svelte"
|
||||||
|
@ -9,6 +10,8 @@
|
||||||
|
|
||||||
const { open, close } = getContext("simple-modal")
|
const { open, close } = getContext("simple-modal")
|
||||||
|
|
||||||
|
$: selectedModel = $backendUiStore.selectedModel
|
||||||
|
|
||||||
const createNewRecord = () => {
|
const createNewRecord = () => {
|
||||||
open(
|
open(
|
||||||
CreateEditRecordModal,
|
CreateEditRecordModal,
|
||||||
|
@ -18,25 +21,11 @@
|
||||||
{ styleContent: { padding: "0" } }
|
{ styleContent: { padding: "0" } }
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
export let selectedDatabase
|
|
||||||
|
|
||||||
let selectedRecord
|
|
||||||
|
|
||||||
$: breadcrumbs = $backendUiStore.breadcrumbs.join(" / ")
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="database-actions">
|
{#if selectedModel.schema && Object.keys(selectedModel.schema).length === 0}
|
||||||
<div class="budibase__label--big">{breadcrumbs}</div>
|
<EmptyModel />
|
||||||
{#if $backendUiStore.selectedModel._id}
|
{:else if $backendUiStore.selectedDatabase._id && selectedModel.name}
|
||||||
<ActionButton primary on:click={createNewRecord}>
|
|
||||||
Create new record
|
|
||||||
</ActionButton>
|
|
||||||
{/if}
|
|
||||||
</div>
|
|
||||||
{#if $backendUiStore.selectedModel.schema && Object.keys($backendUiStore.selectedModel.schema).length === 0}
|
|
||||||
<NewModel />
|
|
||||||
{:else if $backendUiStore.selectedDatabase._id && $backendUiStore.selectedModel.name}
|
|
||||||
<ModelDataTable />
|
<ModelDataTable />
|
||||||
{:else}
|
{:else}
|
||||||
<i style="color: var(--grey-dark)">
|
<i style="color: var(--grey-dark)">
|
||||||
|
@ -45,8 +34,8 @@
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
.database-actions {
|
i {
|
||||||
display: flex;
|
font-size: 20px;
|
||||||
justify-content: space-between;
|
margin-right: 10px;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
Loading…
Reference in New Issue