UI structure
This commit is contained in:
parent
d905677faf
commit
8cb49fb27e
|
@ -47,6 +47,7 @@
|
|||
"safe-buffer": "^5.1.2",
|
||||
"shortid": "^2.2.8",
|
||||
"string_decoder": "^1.2.0",
|
||||
"svelte-routing": "^1.4.2",
|
||||
"uikit": "^3.1.7"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
|
|
@ -165,7 +165,7 @@ export default {
|
|||
svelte({
|
||||
// enable run-time checks when not in production
|
||||
dev: !production,
|
||||
include: "src/**/*.svelte",
|
||||
include: ["src/**/*.svelte", "node_modules/**/*.svelte"],
|
||||
// we'll extract any component CSS out into
|
||||
// a separate file — better for performance
|
||||
css: css => {
|
||||
|
|
|
@ -29,16 +29,16 @@
|
|||
{/await}
|
||||
|
||||
<!--
|
||||
<div class="settings">
|
||||
<IconButton icon="settings"
|
||||
on:click={store.showSettings}/>
|
||||
</div>
|
||||
<div class="settings">
|
||||
<IconButton icon="settings"
|
||||
on:click={store.showSettings}/>
|
||||
</div>
|
||||
|
||||
|
||||
{#if $store.useAnalytics}
|
||||
<iframe src="https://marblekirby.github.io/bb-analytics.html" width="0" height="0" style="visibility:hidden;display:none"/>
|
||||
{/if}
|
||||
-->
|
||||
{#if $store.useAnalytics}
|
||||
<iframe src="https://marblekirby.github.io/bb-analytics.html" width="0" height="0" style="visibility:hidden;display:none"/>
|
||||
{/if}
|
||||
-->
|
||||
</main>
|
||||
|
||||
<style>
|
||||
|
|
|
@ -8,11 +8,9 @@
|
|||
import ComingSoon from "./common/ComingSoon.svelte"
|
||||
|
||||
import { store } from "./builderStore"
|
||||
import { setContext } from 'svelte';
|
||||
import { setContext } from "svelte"
|
||||
|
||||
let activeNav = "database";
|
||||
|
||||
setContext("activeNav", activeNav);
|
||||
let activeNav = "database"
|
||||
</script>
|
||||
|
||||
<div class="root">
|
||||
|
@ -20,14 +18,13 @@
|
|||
<BackendNav />
|
||||
</div>
|
||||
<div class="content">
|
||||
<!-- {#if activeNav === 'database'} -->
|
||||
<Database />
|
||||
<!-- {:else if activeNav === 'actions'}
|
||||
<Database />
|
||||
</div>
|
||||
<!-- {:else if activeNav === 'actions'}
|
||||
<ActionsAndTriggers />
|
||||
{:else if activeNav === 'access levels'}
|
||||
<AccessLevels />
|
||||
{/if} -->
|
||||
</div>
|
||||
<div class="nav">
|
||||
<SchemaManagementDrawer />
|
||||
</div>
|
||||
|
|
|
@ -98,4 +98,24 @@
|
|||
background: #fff;
|
||||
border-radius: 5px;
|
||||
box-shadow: 0 0px 6px rgba(0, 0, 0, 0.05);
|
||||
}
|
||||
|
||||
.budibase__table {
|
||||
border: 1px solid #ccc;
|
||||
background: #fff;
|
||||
border-radius: 2px;
|
||||
}
|
||||
|
||||
.budibase__table thead {
|
||||
background: var(--background-button);
|
||||
}
|
||||
|
||||
.budibase__table th {
|
||||
color: var(--button-text);
|
||||
text-transform: capitalize;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.budibase__table tr {
|
||||
border-bottom: 1px solid #ccc;
|
||||
}
|
|
@ -148,8 +148,6 @@ export const getStore = () => {
|
|||
store.addStylesheet = addStylesheet(store)
|
||||
store.removeStylesheet = removeStylesheet(store)
|
||||
store.savePage = savePage(store)
|
||||
// store.showFrontend = showFrontend(store)
|
||||
// store.showBackend = showBackend(store)
|
||||
store.showSettings = showSettings(store)
|
||||
store.useAnalytics = useAnalytics(store)
|
||||
store.createGeneratedComponents = createGeneratedComponents(store)
|
||||
|
|
|
@ -2,24 +2,17 @@
|
|||
import { JavaScriptIcon } from "../common/Icons"
|
||||
// todo: use https://ace.c9.io
|
||||
export let text = ""
|
||||
export let label = ""
|
||||
export let javascript = false
|
||||
</script>
|
||||
|
||||
<div class="header">
|
||||
{#if javascript}
|
||||
<JavaScriptIcon />
|
||||
{/if}
|
||||
<span>{label}</span>
|
||||
</div>
|
||||
<textarea class="uk-textarea" bind:value={text} />
|
||||
|
||||
<style>
|
||||
textarea {
|
||||
padding: 3px;
|
||||
padding: 10px;
|
||||
margin-top: 5px;
|
||||
margin-bottom: 10px;
|
||||
background: var(--lightslate);
|
||||
background: var(--primary100);
|
||||
color: var(--white);
|
||||
font-family: "Courier New", Courier, monospace;
|
||||
width: 95%;
|
||||
height: 100px;
|
||||
|
|
|
@ -1,10 +1,18 @@
|
|||
<script>
|
||||
import getIcon from "./icon"
|
||||
|
||||
export let icon
|
||||
export let value
|
||||
</script>
|
||||
|
||||
<div class="select-container">
|
||||
<select on:change bind:value>
|
||||
{#if icon}
|
||||
<i class={icon} />
|
||||
{/if}
|
||||
<select
|
||||
class:adjusted={icon}
|
||||
on:change bind:value
|
||||
>
|
||||
<slot />
|
||||
</select>
|
||||
<span class="arrow">
|
||||
|
@ -22,6 +30,16 @@
|
|||
min-width: 200px;
|
||||
}
|
||||
|
||||
.adjusted {
|
||||
padding-left: 2.5em;
|
||||
}
|
||||
|
||||
i {
|
||||
position: absolute;
|
||||
left: 8px;
|
||||
top: 8px;
|
||||
}
|
||||
|
||||
select {
|
||||
height: 35px;
|
||||
display: block;
|
||||
|
|
|
@ -0,0 +1,45 @@
|
|||
<script>
|
||||
export let data = []
|
||||
export let headers = []
|
||||
</script>
|
||||
|
||||
<table class="uk-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Edit</th>
|
||||
{#each headers as header}
|
||||
<th>{header}</th>
|
||||
{/each}
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{#each data as row}
|
||||
<tr>
|
||||
<td>
|
||||
<div class="uk-inline">
|
||||
<i class="ri-more-line" />
|
||||
<div uk-dropdown="mode: click">
|
||||
<ul class="uk-nav uk-dropdown-nav">
|
||||
<li>
|
||||
<div>View</div>
|
||||
</li>
|
||||
<li>
|
||||
<div>Edit</div>
|
||||
</li>
|
||||
<li>
|
||||
<div on:click={() => (deleteRecordModal = true)}>Delete</div>
|
||||
</li>
|
||||
<li>
|
||||
<div>Duplicate</div>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
{#each headers as header}
|
||||
<td>{row[header]}</td>
|
||||
{/each}
|
||||
</tr>
|
||||
{/each}
|
||||
</tbody>
|
||||
</table>
|
|
@ -1,12 +1,13 @@
|
|||
<script>
|
||||
import HierarchyRow from "./HierarchyRow.svelte"
|
||||
import RecordView from "./RecordView.svelte"
|
||||
import ModelView from "./ModelView.svelte"
|
||||
import IndexView from "./IndexView.svelte"
|
||||
import ModelDataTable from "./ModelDataTable"
|
||||
import ActionsHeader from "./ActionsHeader.svelte"
|
||||
import { store } from "../builderStore"
|
||||
import getIcon from "../common/icon"
|
||||
import DropdownButton from "../common/DropdownButton.svelte"
|
||||
import Modal from "../common/Modal.svelte"
|
||||
import { hierarchy as hierarchyFunctions } from "../../../core/src"
|
||||
|
||||
const hierarchyWidth = "200px"
|
||||
|
@ -53,18 +54,24 @@
|
|||
</script>
|
||||
|
||||
<div class="root">
|
||||
<div class="actions-header">
|
||||
<!-- <div class="actions-header">
|
||||
{#if $store.currentNode}
|
||||
<ActionsHeader left={hierarchyWidth} />
|
||||
<ActionsHeader />
|
||||
{/if}
|
||||
</div>
|
||||
</div> -->
|
||||
<div class="node-view">
|
||||
{#if !$store.currentNode}
|
||||
<ModelDataTable />
|
||||
{:else if $store.currentNode.type === 'record'}
|
||||
<RecordView />
|
||||
{:else}
|
||||
<IndexView />
|
||||
<div class="breadcrumbs">{$store.currentlySelectedDatabase}</div>
|
||||
<ModelDataTable />
|
||||
{#if $store.currentNode}
|
||||
<Modal isOpen={$store.currentNode}>
|
||||
{#if $store.currentNode.type === 'record'}
|
||||
<ModelView />
|
||||
<ActionsHeader />
|
||||
{:else}
|
||||
<IndexView />
|
||||
<ActionsHeader />
|
||||
{/if}
|
||||
</Modal>
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -68,20 +68,14 @@
|
|||
|
||||
<ErrorsBox {errors} />
|
||||
|
||||
<form class="uk-form-horizontal">
|
||||
|
||||
<form class="uk-form-stacked">
|
||||
<Textbox label="Name" bind:text={clonedField.name} />
|
||||
<Dropdown
|
||||
label="Type"
|
||||
bind:selected={clonedField.type}
|
||||
options={keys(allTypes)}
|
||||
on:change={typeChanged} />
|
||||
|
||||
{#if isNew}
|
||||
<Textbox label="Field Name" bind:text={clonedField.name} />
|
||||
{:else}
|
||||
<div style="font-weight: bold">{clonedField.name}</div>
|
||||
{/if}
|
||||
|
||||
<Textbox label="Label" bind:text={clonedField.label} />
|
||||
|
||||
{#if clonedField.type === 'string'}
|
||||
|
@ -89,7 +83,7 @@
|
|||
label="Max Length"
|
||||
bind:value={clonedField.typeOptions.maxLength} />
|
||||
<ValuesList
|
||||
label="Values (options)"
|
||||
label="Categories"
|
||||
bind:values={clonedField.typeOptions.values} />
|
||||
<Checkbox
|
||||
label="Declared Values Only"
|
||||
|
|
|
@ -9,20 +9,30 @@
|
|||
|
||||
const pipe = common.$
|
||||
|
||||
const SNIPPET_EDITORS = {
|
||||
MAP: "Map",
|
||||
FILTER: "Filter",
|
||||
SHARD: "Shard Name",
|
||||
}
|
||||
|
||||
let index
|
||||
let indexableRecords = []
|
||||
let currentSnippetEditor = SNIPPET_EDITORS.MAP
|
||||
|
||||
store.subscribe($store => {
|
||||
index = $store.currentNode
|
||||
indexableRecords = pipe($store.hierarchy, [
|
||||
hierarchyFunctions.getFlattenedHierarchy,
|
||||
filter(hierarchyFunctions.isDecendant(index.parent())),
|
||||
filter(hierarchyFunctions.isRecord),
|
||||
map(n => ({
|
||||
node: n,
|
||||
isallowed: some(id => n.nodeId === id)(index.allowedRecordNodeIds),
|
||||
})),
|
||||
])
|
||||
indexableRecords = pipe(
|
||||
$store.hierarchy,
|
||||
[
|
||||
hierarchyFunctions.getFlattenedHierarchy,
|
||||
filter(hierarchyFunctions.isDecendant(index.parent())),
|
||||
filter(hierarchyFunctions.isRecord),
|
||||
map(n => ({
|
||||
node: n,
|
||||
isallowed: some(id => n.nodeId === id)(index.allowedRecordNodeIds),
|
||||
})),
|
||||
]
|
||||
)
|
||||
})
|
||||
|
||||
const toggleAllowedRecord = record => {
|
||||
|
@ -36,11 +46,28 @@
|
|||
}
|
||||
</script>
|
||||
|
||||
<form class="uk-form-horizontal root">
|
||||
<Textbox bind:text={index.name} label="Name" />
|
||||
<h3 class="budibase__title--3">
|
||||
<i class="ri-eye-line" />
|
||||
Create / Edit View
|
||||
</h3>
|
||||
<form class="uk-form-stacked root">
|
||||
<h4 class="budibase__label--big">Settings</h4>
|
||||
<div class="uk-grid-small" uk-grid>
|
||||
<div class="uk-width-1-2@s">
|
||||
<Textbox bind:text={index.name} label="Name" />
|
||||
</div>
|
||||
<div class="uk-width-1-2@s">
|
||||
<Dropdown
|
||||
label="View Type"
|
||||
bind:selected={index.indexType}
|
||||
options={['ancestor', 'reference']} />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="allowed-records">
|
||||
<div class="index-label">Records to Index</div>
|
||||
<div class="budibase__label--big">
|
||||
Which models would you like to add to this view?
|
||||
</div>
|
||||
{#each indexableRecords as rec}
|
||||
<input
|
||||
type="checkbox"
|
||||
|
@ -50,14 +77,22 @@
|
|||
{/each}
|
||||
</div>
|
||||
|
||||
<Dropdown
|
||||
label="Index Type"
|
||||
bind:selected={index.indexType}
|
||||
options={['ancestor', 'reference']} />
|
||||
|
||||
<CodeArea bind:text={index.map} javascript label="Map" />
|
||||
<CodeArea bind:text={index.filter} javascript label="Filter" />
|
||||
<CodeArea javascript bind:text={index.getShardName} label="Shard Name" />
|
||||
<h4 class="budibase__label--big">Snippets</h4>
|
||||
{#each Object.values(SNIPPET_EDITORS) as snippetType}
|
||||
<span
|
||||
class="snippet-selector__heading hoverable"
|
||||
class:highlighted={currentSnippetEditor === snippetType}
|
||||
on:click={() => (currentSnippetEditor = snippetType)}>
|
||||
{snippetType}
|
||||
</span>
|
||||
{/each}
|
||||
{#if currentSnippetEditor === SNIPPET_EDITORS.MAP}
|
||||
<CodeArea bind:text={index.map} label="Map" />
|
||||
{:else if currentSnippetEditor === SNIPPET_EDITORS.FILTER}
|
||||
<CodeArea bind:text={index.filter} label="Filter" />
|
||||
{:else if currentSnippetEditor === SNIPPET_EDITORS.SHARD}
|
||||
<CodeArea bind:text={index.getShardName} label="Shard Name" />
|
||||
{/if}
|
||||
|
||||
</form>
|
||||
|
||||
|
@ -79,4 +114,13 @@
|
|||
color: #333;
|
||||
font-size: 0.875rem;
|
||||
}
|
||||
|
||||
.snippet-selector__heading {
|
||||
margin-right: 20px;
|
||||
opacity: 0.7;
|
||||
}
|
||||
|
||||
.highlighted {
|
||||
opacity: 1;
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -1,6 +1,11 @@
|
|||
<script>
|
||||
import { store } from "../../builderStore"
|
||||
import Select from "../../common/Select.svelte"
|
||||
import { CreateEditRecordModal, DeleteRecordModal } from "./modals"
|
||||
import ActionButton from "../../common/ActionButton.svelte"
|
||||
import TablePagination from "./TablePagination.svelte"
|
||||
import * as api from "./api"
|
||||
import { getIndexSchema } from "../../common/core"
|
||||
|
||||
let pages = [1, 2, 3]
|
||||
|
||||
|
@ -10,14 +15,31 @@
|
|||
{ name: "Martin", inStock: true },
|
||||
]
|
||||
export let headers = ["name", "inStock"]
|
||||
export let pageSize = 10
|
||||
// export let pageSize = 10
|
||||
|
||||
let selectedView = ""
|
||||
let modalOpen = false
|
||||
let deleteRecordModal = false
|
||||
|
||||
$: indexes = $store.hierarchy.indexes
|
||||
|
||||
const getSchema = getIndexSchema($store.hierarchy)
|
||||
</script>
|
||||
|
||||
<CreateEditRecordModal bind:modalOpen />
|
||||
<DeleteRecordModal modalOpen={deleteRecordModal} />
|
||||
|
||||
<section>
|
||||
<h4 class="budibase__title--3">Shoe database</h4>
|
||||
<div class="table-controls">
|
||||
<Select />
|
||||
<ActionButton primary>Create new record</ActionButton>
|
||||
<h4 class="budibase__title--3">Shoe database</h4>
|
||||
<Select
|
||||
icon="ri-eye-line"
|
||||
on:change={e => api.fetchDataForView(e.target.value)}>
|
||||
{#each indexes as index}
|
||||
({console.log(getSchema(index))})
|
||||
<option value={index.name}>{index.name}</option>
|
||||
{/each}
|
||||
</Select>
|
||||
</div>
|
||||
<table class="uk-table">
|
||||
<thead>
|
||||
|
@ -32,7 +54,25 @@
|
|||
{#each data as row}
|
||||
<tr>
|
||||
<td>
|
||||
<i class="ri-more-line" />
|
||||
<div class="uk-inline">
|
||||
<i class="ri-more-line" />
|
||||
<div uk-dropdown="mode: click">
|
||||
<ul class="uk-nav uk-dropdown-nav">
|
||||
<li>
|
||||
<div>View</div>
|
||||
</li>
|
||||
<li>
|
||||
<div on:click={() => (modalOpen = true)}>Edit</div>
|
||||
</li>
|
||||
<li>
|
||||
<div on:click={() => (deleteRecordModal = true)}>Delete</div>
|
||||
</li>
|
||||
<li>
|
||||
<div>Duplicate</div>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
{#each headers as header}
|
||||
<td>{row[header]}</td>
|
||||
|
@ -41,13 +81,7 @@
|
|||
{/each}
|
||||
</tbody>
|
||||
</table>
|
||||
<div class="pagination">
|
||||
<button>Previous</button>
|
||||
<button>Next</button>
|
||||
<!-- {#each data as page}
|
||||
<button>{page}</button>
|
||||
{/each} -->
|
||||
</div>
|
||||
<TablePagination {data} />
|
||||
</section>
|
||||
|
||||
<style>
|
||||
|
@ -76,4 +110,8 @@
|
|||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.ri-more-line:hover, .uk-dropdown-nav li:hover{
|
||||
cursor: pointer;
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -0,0 +1,44 @@
|
|||
<script>
|
||||
export let data = []
|
||||
</script>
|
||||
|
||||
<div class="pagination">
|
||||
<div class="pagination__buttons">
|
||||
<button>Previous</button>
|
||||
<button>Next</button>
|
||||
{#each data as page, idx}
|
||||
<button>{idx + 1}</button>
|
||||
{/each}
|
||||
</div>
|
||||
<p>Showing 10 (hardcoded, update this) of {data.length} entries</p>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.pagination {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.pagination__buttons button {
|
||||
display: inline-block;
|
||||
padding: 10px;
|
||||
margin: 0;
|
||||
background: #fff;
|
||||
border: 1px solid #ccc;
|
||||
text-transform: capitalize;
|
||||
border-radius: 5px;
|
||||
font-family: Roboto;
|
||||
min-width: 20px;
|
||||
}
|
||||
|
||||
.pagination__buttons button:hover {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.ri-more-line:hover {
|
||||
cursor: pointer;
|
||||
}
|
||||
</style>
|
|
@ -0,0 +1,24 @@
|
|||
import api from "../../builderStore/api";
|
||||
|
||||
export async function deleteRecord(appName, appInstanceId, record) {
|
||||
const DELETE_RECORDS_URL = `/_builder/instance/${appName}/${appInstanceId}/api/record/${record.name}/${record.id}`
|
||||
const response = await api.delete({
|
||||
url: DELETE_RECORDS_URL
|
||||
});
|
||||
}
|
||||
|
||||
export async function createNewRecord(record) {
|
||||
console.log(record);
|
||||
}
|
||||
|
||||
export async function fetchDataForView(viewName) {
|
||||
console.log(viewName);
|
||||
// const FETCH_RECORDS_URL = `/_builder/instance/${}/${}/api/listRecords/`
|
||||
|
||||
// const response = await api.get({ url: FETCH_RECORDS_URL });
|
||||
|
||||
// console.log(response);
|
||||
|
||||
// GET /_builder/instance/:appname/:instanceid/api/listRecords/contacts/abcd1234/all_deals
|
||||
|
||||
}
|
|
@ -0,0 +1,23 @@
|
|||
<script>
|
||||
import Modal from "../../common/Modal.svelte"
|
||||
import ActionButton from "../../common/ActionButton.svelte"
|
||||
import * as api from "./api"
|
||||
|
||||
export let modalOpen = false
|
||||
|
||||
let recordInfo = {}
|
||||
|
||||
const onClosed = () => (modalOpen = false)
|
||||
</script>
|
||||
|
||||
<Modal {onClosed} bind:isOpen={modalOpen} title={"Create / Edit Field"}>
|
||||
|
||||
<div class="actions">
|
||||
<ActionButton alert on:click={onClosed}>Cancel</ActionButton>
|
||||
<ActionButton
|
||||
disabled={false}
|
||||
on:click={() => api.createNewRecord(recordInfo)}>
|
||||
Save
|
||||
</ActionButton>
|
||||
</div>
|
||||
</Modal>
|
|
@ -0,0 +1,23 @@
|
|||
<script>
|
||||
import Modal from "../../common/Modal.svelte"
|
||||
import ActionButton from "../../common/ActionButton.svelte"
|
||||
import * as api from "./api"
|
||||
|
||||
export let modalOpen = false
|
||||
|
||||
let recordInfo = {}
|
||||
|
||||
const onClosed = () => (modalOpen = false)
|
||||
</script>
|
||||
|
||||
<Modal {onClosed} bind:isOpen={modalOpen} title={'Record'}>
|
||||
<h4 class="budibase__title--4">Create / Edit Record</h4>
|
||||
<div class="actions">
|
||||
<ActionButton alert on:click={onClosed}>Cancel</ActionButton>
|
||||
<ActionButton
|
||||
disabled={false}
|
||||
on:click={() => api.createNewRecord(recordInfo)}>
|
||||
Save
|
||||
</ActionButton>
|
||||
</div>
|
||||
</Modal>
|
|
@ -0,0 +1,60 @@
|
|||
<script>
|
||||
import Modal from "../../../common/Modal.svelte"
|
||||
import ActionButton from "../../../common/ActionButton.svelte"
|
||||
import * as api from "../api"
|
||||
|
||||
export let modalOpen = false
|
||||
export let model = {}
|
||||
|
||||
const onClosed = () => (modalOpen = false)
|
||||
</script>
|
||||
|
||||
<Modal {onClosed} bind:isOpen={modalOpen} title={'Record'}>
|
||||
<h4 class="budibase__title--4">Create / Edit Record</h4>
|
||||
<div class="actions">
|
||||
<form>
|
||||
<div class="uk-margin">
|
||||
<label class="uk-form-label" for="form-stacked-text">Text</label>
|
||||
<div class="uk-form-controls">
|
||||
<input
|
||||
class="uk-input"
|
||||
id="form-stacked-text"
|
||||
type="text"
|
||||
placeholder="Some text..." />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="uk-margin">
|
||||
<label class="uk-form-label" for="form-stacked-select">Select</label>
|
||||
<div class="uk-form-controls">
|
||||
<select class="uk-select" id="form-stacked-select">
|
||||
<option>Option 01</option>
|
||||
<option>Option 02</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="uk-margin">
|
||||
<div class="uk-form-label">Radio</div>
|
||||
<div class="uk-form-controls">
|
||||
<label>
|
||||
<input class="uk-radio" type="radio" name="radio1" />
|
||||
Option 01
|
||||
</label>
|
||||
<br />
|
||||
<label>
|
||||
<input class="uk-radio" type="radio" name="radio1" />
|
||||
Option 02
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</form>
|
||||
<ActionButton alert on:click={onClosed}>Cancel</ActionButton>
|
||||
<ActionButton
|
||||
disabled={false}
|
||||
on:click={() => api.createNewRecord(recordInfo)}>
|
||||
Save
|
||||
</ActionButton>
|
||||
</div>
|
||||
</Modal>
|
|
@ -0,0 +1,23 @@
|
|||
<script>
|
||||
import Modal from "../../common/Modal.svelte"
|
||||
import ActionButton from "../../common/ActionButton.svelte"
|
||||
import * as api from "./api"
|
||||
|
||||
export let modalOpen = false
|
||||
|
||||
let recordInfo = {}
|
||||
|
||||
const onClosed = () => (modalOpen = false)
|
||||
</script>
|
||||
|
||||
<Modal {onClosed} bind:isOpen={modalOpen} title={'Record'}>
|
||||
|
||||
<div class="actions">
|
||||
<ActionButton alert on:click={onClosed}>Cancel</ActionButton>
|
||||
<ActionButton
|
||||
disabled={false}
|
||||
on:click={() => api.createNewRecord(recordInfo)}>
|
||||
Save
|
||||
</ActionButton>
|
||||
</div>
|
||||
</Modal>
|
|
@ -0,0 +1,24 @@
|
|||
<script>
|
||||
import Modal from "../../../common/Modal.svelte"
|
||||
import ActionButton from "../../../common/ActionButton.svelte"
|
||||
import * as api from "../api"
|
||||
|
||||
export let modalOpen = false
|
||||
|
||||
let recordInfo = {}
|
||||
|
||||
const onClosed = () => (modalOpen = false)
|
||||
</script>
|
||||
|
||||
<Modal {onClosed} bind:isOpen={modalOpen} title={"Delete Record?"}>
|
||||
<h4 class="budibase__title--4">Delete Record</h4>
|
||||
Are you sure you want to delete this record? All of your data will be permanently removed. This action cannot be undone.
|
||||
<div class="modal-actions">
|
||||
<ActionButton on:click={onClosed}>Cancel</ActionButton>
|
||||
<ActionButton
|
||||
alert
|
||||
on:click={() => api.deleteRecord(recordInfo)}>
|
||||
Delete
|
||||
</ActionButton>
|
||||
</div>
|
||||
</Modal>
|
|
@ -0,0 +1,2 @@
|
|||
export { default as DeleteRecordModal } from "./DeleteRecord.svelte";
|
||||
export { default as CreateEditRecordModal } from "./CreateEditRecord.svelte";
|
|
@ -24,11 +24,14 @@
|
|||
record = $store.currentNode
|
||||
const flattened = h.getFlattenedHierarchy($store.hierarchy)
|
||||
getIndexAllowedRecords = index =>
|
||||
pipe(index.allowedRecordNodeIds, [
|
||||
filter(id => some(n => n.nodeId === id)(flattened)),
|
||||
map(id => find(n => n.nodeId === id)(flattened).name),
|
||||
join(", "),
|
||||
])
|
||||
pipe(
|
||||
index.allowedRecordNodeIds,
|
||||
[
|
||||
filter(id => some(n => n.nodeId === id)(flattened)),
|
||||
map(id => find(n => n.nodeId === id)(flattened).name),
|
||||
join(", "),
|
||||
]
|
||||
)
|
||||
|
||||
newField = () => {
|
||||
isNewField = true
|
||||
|
@ -72,16 +75,19 @@
|
|||
}
|
||||
|
||||
let getTypeOptions = typeOptions =>
|
||||
pipe(typeOptions, [
|
||||
keys,
|
||||
map(
|
||||
k =>
|
||||
`<span style="color:var(--slate)">${k}: </span>${getTypeOptionsValueText(
|
||||
typeOptions[k]
|
||||
)}`
|
||||
),
|
||||
join("<br>"),
|
||||
])
|
||||
pipe(
|
||||
typeOptions,
|
||||
[
|
||||
keys,
|
||||
map(
|
||||
k =>
|
||||
`<span style="color:var(--slate)">${k}: </span>${getTypeOptionsValueText(
|
||||
typeOptions[k]
|
||||
)}`
|
||||
),
|
||||
join("<br>"),
|
||||
]
|
||||
)
|
||||
|
||||
const nameChanged = ev => {
|
||||
const pluralName = n => `${n}s`
|
||||
|
@ -93,68 +99,78 @@
|
|||
|
||||
<div class="root">
|
||||
|
||||
<form class="uk-form-horizontal">
|
||||
<h3 class="budibase__title--3">Settings</h3>
|
||||
<form class="uk-form-stacked">
|
||||
<h3 class="budibase__title--3">
|
||||
<i class="ri-list-settings-line" />
|
||||
Create / Edit Model
|
||||
</h3>
|
||||
|
||||
<Textbox label="Name:" bind:text={record.name} on:change={nameChanged} />
|
||||
<h3 class="budibase__label--big">Settings</h3>
|
||||
|
||||
<Textbox label="Name" bind:text={record.name} on:change={nameChanged} />
|
||||
{#if !record.isSingle}
|
||||
<Textbox label="Collection Name:" bind:text={record.collectionName} />
|
||||
<Textbox label="Collection Name" bind:text={record.collectionName} />
|
||||
<Textbox
|
||||
label="Estimated Record Count:"
|
||||
label="Estimated Record Count"
|
||||
bind:text={record.estimatedRecordCount} />
|
||||
{/if}
|
||||
<div class="recordkey">{record.nodeKey()}</div>
|
||||
|
||||
</form>
|
||||
<h3 class="budibase__title--3">
|
||||
<div class="table-controls">
|
||||
<span class="budibase__label--big">Fields</span>
|
||||
<h4 class="hoverable" on:click={newField}>Add new field</h4>
|
||||
</div>
|
||||
<!-- <h3 class="budibase__label--big">
|
||||
Fields
|
||||
<span class="add-field-button" on:click={newField}>
|
||||
{@html getIcon('plus')}
|
||||
</span>
|
||||
</h3>
|
||||
</h3> -->
|
||||
|
||||
{#if record.fields.length > 0}
|
||||
<table class="fields-table uk-table">
|
||||
<thead>
|
||||
<table class="fields-table uk-table budibase__table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Edit</th>
|
||||
<th>Name</th>
|
||||
<th>Type</th>
|
||||
<th>Values</th>
|
||||
<th />
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{#each record.fields as field}
|
||||
<tr>
|
||||
<th>Name</th>
|
||||
<th>Type</th>
|
||||
<th>Options</th>
|
||||
<th />
|
||||
<td>
|
||||
<i class="ri-more-line" on:click={() => editField(field)} />
|
||||
</td>
|
||||
<td>
|
||||
<div>{field.name}</div>
|
||||
</td>
|
||||
<td>{field.type}</td>
|
||||
<td>({console.log(field.typeOptions)}) {field.typeOptions.values}</td>
|
||||
<td>
|
||||
<!-- <span class="edit-button" on:click={() => editField(field)}>
|
||||
{@html getIcon('edit')}
|
||||
</span> -->
|
||||
<span class="edit-button" on:click={() => deleteField(field)}>
|
||||
{@html getIcon('trash')}
|
||||
</span>
|
||||
</td>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{#each record.fields as field}
|
||||
<tr>
|
||||
<td>
|
||||
<div class="field-label">{field.label}</div>
|
||||
<div style="font-size: 0.8em; color: var(--slate)">
|
||||
{field.name}
|
||||
</div>
|
||||
</td>
|
||||
<td>{field.type}</td>
|
||||
<td>
|
||||
{@html getTypeOptions(field.typeOptions)}
|
||||
</td>
|
||||
<td>
|
||||
<span class="edit-button" on:click={() => editField(field)}>
|
||||
{@html getIcon('edit')}
|
||||
</span>
|
||||
<span class="edit-button" on:click={() => deleteField(field)}>
|
||||
{@html getIcon('trash')}
|
||||
</span>
|
||||
</td>
|
||||
</tr>
|
||||
{/each}
|
||||
</tbody>
|
||||
</table>
|
||||
{:else}(no fields added){/if}
|
||||
{/each}
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
{#if editingField}
|
||||
<Modal
|
||||
title="Manage Index Fields"
|
||||
bind:isOpen={editingField}
|
||||
onClosed={() => onFinishedFieldEdit(false)}>
|
||||
|
||||
<h3 class="budibase__title--3">
|
||||
<i class="ri-file-list-line" />
|
||||
Create / Edit Field
|
||||
</h3>
|
||||
<FieldView
|
||||
field={fieldToEdit}
|
||||
onFinished={onFinishedFieldEdit}
|
||||
|
@ -163,9 +179,9 @@
|
|||
</Modal>
|
||||
{/if}
|
||||
|
||||
<h3 class="budibase__title--3">Indexes</h3>
|
||||
<!-- <h3 class="budibase__title--3">Indexes</h3> -->
|
||||
|
||||
{#each record.indexes as index}
|
||||
<!-- {#each record.indexes as index}
|
||||
<div class="index-container">
|
||||
<div class="index-name">
|
||||
{index.name}
|
||||
|
@ -192,7 +208,7 @@
|
|||
</div>
|
||||
{:else}
|
||||
<div class="no-indexes">No indexes added.</div>
|
||||
{/each}
|
||||
{/each} -->
|
||||
|
||||
</div>
|
||||
|
||||
|
@ -213,10 +229,6 @@
|
|||
border-collapse: collapse;
|
||||
}
|
||||
|
||||
.add-field-button {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.edit-button {
|
||||
cursor: pointer;
|
||||
color: var(--secondary25);
|
||||
|
@ -227,35 +239,6 @@
|
|||
color: var(--secondary75);
|
||||
}
|
||||
|
||||
th {
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
td {
|
||||
padding: 1rem 5rem 1rem 0rem;
|
||||
margin: 0;
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.field-label {
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
thead > tr {
|
||||
border-width: 0px 0px 1px 0px;
|
||||
border-style: solid;
|
||||
border-color: var(--secondary75);
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
tbody > tr {
|
||||
border-width: 0px 0px 1px 0px;
|
||||
border-style: solid;
|
||||
border-color: var(--primary10);
|
||||
}
|
||||
|
||||
tbody > tr:hover {
|
||||
background-color: var(--primary10);
|
||||
}
|
||||
|
@ -264,38 +247,17 @@
|
|||
color: var(--secondary75);
|
||||
}
|
||||
|
||||
.index-container {
|
||||
border-style: solid;
|
||||
border-width: 0 0 1px 0;
|
||||
border-color: var(--secondary25);
|
||||
padding: 10px;
|
||||
margin-bottom: 5px;
|
||||
.table-controls {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.index-label {
|
||||
color: var(--slate);
|
||||
.ri-more-line:hover {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.index-name {
|
||||
font-weight: bold;
|
||||
color: var(--primary100);
|
||||
}
|
||||
|
||||
.index-container code {
|
||||
h4 {
|
||||
margin: 0;
|
||||
display: inline;
|
||||
background-color: var(--primary10);
|
||||
color: var(--secondary100);
|
||||
padding: 3px;
|
||||
}
|
||||
|
||||
.index-field-row {
|
||||
margin: 1rem 0rem 0rem 0rem;
|
||||
}
|
||||
|
||||
.no-indexes {
|
||||
margin: 1rem 0rem 0rem 0rem;
|
||||
font-family: var(--fontnormal);
|
||||
font-size: 14px;
|
||||
}
|
||||
</style>
|
|
@ -102,4 +102,8 @@ h5 {
|
|||
font-family: var(--fontblack);
|
||||
font-size: 12pt;
|
||||
color: var(--darkslate);
|
||||
}
|
||||
|
||||
.hoverable:hover {
|
||||
cursor: pointer;
|
||||
}
|
|
@ -15,5 +15,5 @@ import "codemirror/theme/monokai.css"
|
|||
|
||||
/* eslint-disable */
|
||||
const app = new App({
|
||||
target: document.getElementById("app"),
|
||||
target: document.getElementById("app")
|
||||
})
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
<script>
|
||||
import { setContext } from "svelte"
|
||||
import { getContext } from "svelte"
|
||||
import { store } from "../builderStore"
|
||||
import HierarchyRow from "./HierarchyRow.svelte"
|
||||
import DatabasesList from "./DatabasesList.svelte"
|
||||
|
@ -35,7 +35,7 @@
|
|||
|
||||
// let newChildActions = defaultNewChildActions
|
||||
|
||||
const setActiveNav = name => () => setContext("activeNav", name)
|
||||
const setActiveNav = name => () => getContext("navigation").setActiveNav(name);
|
||||
|
||||
// store.subscribe(db => {
|
||||
// if (!db.currentNode || hierarchyFunctions.isIndex(db.currentNode)) {
|
||||
|
|
|
@ -1,9 +1,18 @@
|
|||
<script>
|
||||
import { navigate } from "svelte-routing"
|
||||
import { store } from "../builderStore"
|
||||
import getIcon from "../common/icon"
|
||||
import { CheckIcon } from "../common/Icons"
|
||||
|
||||
$: instances = $store.appInstances
|
||||
|
||||
function selectDatabase(databaseId) {
|
||||
store.update(state => {
|
||||
state.currentlySelectedDatabase = databaseId
|
||||
return state
|
||||
})
|
||||
navigate("/database", { replace: true })
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="root">
|
||||
|
@ -11,14 +20,14 @@
|
|||
{#each $store.appInstances as { id, name }}
|
||||
<li>
|
||||
<span class="icon">
|
||||
{#if id === $store.currentPageName}
|
||||
{#if id === $store.currentlySelectedDatabase}
|
||||
<CheckIcon />
|
||||
{/if}
|
||||
</span>
|
||||
|
||||
<button
|
||||
class:active={id === $store.currentPageName}
|
||||
on:click={() => store.setCurrentPage(id)}>
|
||||
class:active={id === $store.currentlySelectedDatabase}
|
||||
on:click={() => selectDatabase(id)}>
|
||||
{name}
|
||||
</button>
|
||||
</li>
|
||||
|
|
|
@ -16,8 +16,7 @@
|
|||
|
||||
store.subscribe(state => {
|
||||
if (state.currentNode) {
|
||||
navActive =
|
||||
getContext("activeNav") === "database" && node.nodeId === state.currentNode.nodeId
|
||||
navActive = node.nodeId === state.currentNode.nodeId
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
|
|
@ -70,7 +70,19 @@
|
|||
<div class="components-list-container">
|
||||
<div class="nav-group-header">
|
||||
<div class="hierarchy-title">Schema</div>
|
||||
<DropdownButton iconName="plus" actions={newChildActions} />
|
||||
<div class="uk-inline">
|
||||
<i class="ri-add-line" />
|
||||
<div uk-dropdown="mode: click">
|
||||
<ul class="uk-nav uk-dropdown-nav">
|
||||
<li>
|
||||
Model
|
||||
</li>
|
||||
<li>
|
||||
View
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
|
|
@ -44,7 +44,7 @@ export const generateSchema = (hierarchy, indexNode) => {
|
|||
keys,
|
||||
map(k => ({ name: k, type: schema[k].name })),
|
||||
filter(s => s.name !== "sortKey"),
|
||||
orderBy("name", ["desc"]), // reverse aplha
|
||||
orderBy("name", ["desc"]), // reverse alpha
|
||||
concat([{ name: "sortKey", type: all.string.name }]), // sortKey on end
|
||||
reverse, // sortKey first, then rest are alphabetical
|
||||
])
|
||||
|
|
Loading…
Reference in New Issue