table enhancements

This commit is contained in:
Martin McKeaveney 2020-03-22 09:21:18 +00:00
parent 9756574e6f
commit b474f8a600
12 changed files with 274 additions and 261 deletions

View File

@ -103,7 +103,9 @@ const lodash_fp_exports = [
"toNumber", "toNumber",
"takeRight", "takeRight",
"toPairs", "toPairs",
"remove" "remove",
"findIndex",
"compose"
] ]
const lodash_exports = [ const lodash_exports = [

View File

@ -107,7 +107,7 @@
} }
.budibase__table thead { .budibase__table thead {
background: var(--background-button); background: #fafafa;
} }
.budibase__table th { .budibase__table th {
@ -119,3 +119,9 @@
.budibase__table tr { .budibase__table tr {
border-bottom: 1px solid #ccc; border-bottom: 1px solid #ccc;
} }
.button--toggled {
background: #fafafa;
color: var(--button-text);
padding: 10px;
}

View File

@ -38,7 +38,7 @@ export const getBackendUiStore = () => {
}, },
records: { records: {
delete: record => store.update(state => { delete: record => store.update(state => {
state.selectedView.records = remove(state.selectedView.records, { key: record.key }); state.selectedView.records = remove(state.selectedView.records, { id: record.id });
return state return state
}), }),
}, },
@ -125,7 +125,6 @@ export const saveCurrentNode = store => () => {
if (errors.length > 0) { if (errors.length > 0) {
return state return state
} }
const parentNode = getNode(state.hierarchy, state.currentNode.parent().nodeId) const parentNode = getNode(state.hierarchy, state.currentNode.parent().nodeId)
const existingNode = getNode(state.hierarchy, state.currentNode.nodeId) const existingNode = getNode(state.hierarchy, state.currentNode.nodeId)
@ -134,10 +133,7 @@ export const saveCurrentNode = store => () => {
if (existingNode) { if (existingNode) {
// remove existing // remove existing
index = existingNode.parent().children.indexOf(existingNode) index = existingNode.parent().children.indexOf(existingNode)
existingNode.parent().children = existingNode.parent().children.filter(c => c.nodeId !== existingNode.nodeId); existingNode.parent().children = existingNode.parent().children.filter(node => node.nodeId !== existingNode.nodeId);
// existingNode.parent().children = pipe(existingNode.parent().children, [
// filter(c => c.nodeId !== existingNode.nodeId),
// ])
} }
// should add node into existing hierarchy // should add node into existing hierarchy
@ -172,7 +168,7 @@ export const deleteCurrentNode = store => () => {
store.update(state => { store.update(state => {
const nodeToDelete = getNode(state.hierarchy, state.currentNode.nodeId) const nodeToDelete = getNode(state.hierarchy, state.currentNode.nodeId)
state.currentNode = hierarchyFunctions.isRoot(nodeToDelete.parent()) state.currentNode = hierarchyFunctions.isRoot(nodeToDelete.parent())
? find(n => n !== state.currentNode)(state.hierarchy.children) ? state.hierarchy.children.find(node => node !== state.currentNode)
: nodeToDelete.parent() : nodeToDelete.parent()
const recordOrIndexKey = hierarchyFunctions.isRecord(nodeToDelete) ? "children" : "indexes"; const recordOrIndexKey = hierarchyFunctions.isRecord(nodeToDelete) ? "children" : "indexes";
@ -212,10 +208,7 @@ export const saveField = databaseStore => field => {
export const deleteField = databaseStore => field => { export const deleteField = databaseStore => field => {
databaseStore.update(db => { databaseStore.update(db => {
db.currentNode.fields = filter(f => f.name !== field.name)( db.currentNode.fields = db.currentNode.fields.filter(f => f.name !== field.name)
db.currentNode.fields
)
return db return db
}) })
} }

View File

@ -31,8 +31,6 @@
$: viewOpen = $backendUiStore.visibleModal === "VIEW" $: viewOpen = $backendUiStore.visibleModal === "VIEW"
$: databaseOpen = $backendUiStore.visibleModal === "DATABASE" $: databaseOpen = $backendUiStore.visibleModal === "DATABASE"
$: deleteRecordOpen = $backendUiStore.visibleModal === "DELETE_RECORD" $: deleteRecordOpen = $backendUiStore.visibleModal === "DELETE_RECORD"
// $: recordOpen = $store.currentNode && $store.currentNode.type === 'record'
// $: viewOpen = $store.currentNode && $store.currentNode.type === 'index'
</script> </script>
<Modal isOpen={!!$backendUiStore.visibleModal} {onClosed}> <Modal isOpen={!!$backendUiStore.visibleModal} {onClosed}>

View File

@ -10,19 +10,23 @@
export let selectRecord export let selectRecord
let pages = [1, 2, 3] const ITEMS_PER_PAGE = 2
let selectedView = "" let selectedView = ""
let modalOpen = false let modalOpen = false
let headers = [] let headers = []
let selectedRecord let selectedRecord
let currentPage = 0
$: views = $store.hierarchy.indexes $: views = $store.hierarchy.indexes
$: currentAppInfo = { $: currentAppInfo = {
appname: $store.appname, appname: $store.appname,
instanceId: $store.currentInstanceId instanceId: $store.currentInstanceId,
} }
$: data = $backendUiStore.selectedView.records $: data = $backendUiStore.selectedView.records.slice(
$: deleteRecordModal = $backendUiStore.visibleModal === "DELETE_RECORD" currentPage * ITEMS_PER_PAGE,
currentPage * ITEMS_PER_PAGE + ITEMS_PER_PAGE
)
const getSchema = getIndexSchema($store.hierarchy) const getSchema = getIndexSchema($store.hierarchy)
@ -44,9 +48,10 @@
}) })
</script> </script>
{#if views.length > 0}
<section> <section>
<div class="table-controls"> <div class="table-controls">
<h4 class="budibase__title--3">Shoe database</h4> <h4 class="budibase__title--3">{$backendUiStore.selectedDatabase.name || ""}</h4>
<Select <Select
icon="ri-eye-line" icon="ri-eye-line"
on:change={e => fetchRecordsForView(e.target.value)}> on:change={e => fetchRecordsForView(e.target.value)}>
@ -65,6 +70,9 @@
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
{#if data.length === 0}
<div class="no-data">No Data.</div>
{/if}
{#each data as row} {#each data as row}
<tr class="hoverable"> <tr class="hoverable">
<td> <td>
@ -72,13 +80,10 @@
<i class="ri-more-line" /> <i class="ri-more-line" />
<div uk-dropdown="mode: click"> <div uk-dropdown="mode: click">
<ul class="uk-nav uk-dropdown-nav"> <ul class="uk-nav uk-dropdown-nav">
<li>
<div>View</div>
</li>
<li <li
on:click={() => { on:click={() => {
selectRecord(row) selectRecord(row)
backendUiStore.actions.modals.show("RECORD") backendUiStore.actions.modals.show('RECORD')
}}> }}>
<div>Edit</div> <div>Edit</div>
</li> </li>
@ -86,13 +91,18 @@
<div <div
on:click={() => { on:click={() => {
selectRecord(row) selectRecord(row)
backendUiStore.actions.modals.show("DELETE_RECORD") backendUiStore.actions.modals.show('DELETE_RECORD')
}}> }}>
Delete Delete
</div> </div>
</li> </li>
<li> <li>
<div>Duplicate</div> <div
on:click={() => {
console.log("DUPLICATION")
}}>
Duplicate
</div>
</li> </li>
</ul> </ul>
</div> </div>
@ -105,15 +115,19 @@
{/each} {/each}
</tbody> </tbody>
</table> </table>
<TablePagination /> <TablePagination bind:currentPage pageItemCount={data.length} {ITEMS_PER_PAGE} />
</section> </section>
{:else}
Please create a model to get started.
{/if}
<style> <style>
table { table {
border: 1px solid #ccc; border: 1px solid #ccc;
background: #fff; background: #fff;
border-radius: 3px; border-radius: 3px;
border-collapse: separate; border-collapse: collapse;
} }
thead { thead {
@ -146,4 +160,8 @@
.uk-dropdown-nav li:hover { .uk-dropdown-nav li:hover {
cursor: pointer; cursor: pointer;
} }
.no-data {
padding: 20px;
}
</style> </style>

View File

@ -1,18 +1,43 @@
<script> <script>
import { backendUiStore } from "../../builderStore" import { backendUiStore } from "../../builderStore"
export let currentPage
export let pageItemCount
export let ITEMS_PER_PAGE
let numPages = 0
$: data = $backendUiStore.selectedView.records $: data = $backendUiStore.selectedView.records
$: numPages = Math.ceil(data.length / ITEMS_PER_PAGE)
const next = () => {
if (currentPage + 1 === numPages) return;
currentPage = currentPage + 1
}
const previous = () => {
if (currentPage == 0) return;
currentPage = currentPage - 1
}
const selectPage = page => {
currentPage = page
}
</script> </script>
<div class="pagination"> <div class="pagination">
<div class="pagination__buttons"> <div class="pagination__buttons">
<button>Previous</button> <button on:click={previous}>Previous</button>
<button>Next</button> <button on:click={next}>Next</button>
{#each data as page, idx} {#each Array(numPages) as _, idx}
<button>{idx + 1}</button> <button
class:selected={idx === currentPage}
on:click={() => selectPage(idx)}>
{idx + 1}
</button>
{/each} {/each}
</div> </div>
<p>Showing 10 (hardcoded, update this) of {data.length} entries</p> <p>Showing {pageItemCount} of {data.length} entries</p>
</div> </div>
<style> <style>
@ -24,6 +49,10 @@
justify-content: center; justify-content: center;
} }
.pagination__buttons {
display: flex;
}
.pagination__buttons button { .pagination__buttons button {
display: inline-block; display: inline-block;
padding: 10px; padding: 10px;
@ -31,16 +60,18 @@
background: #fff; background: #fff;
border: 1px solid #ccc; border: 1px solid #ccc;
text-transform: capitalize; text-transform: capitalize;
border-radius: 5px; border-radius: 3px;
font-family: Roboto; font-family: Roboto;
min-width: 20px; min-width: 20px;
transition: 0.3s background-color;
} }
.pagination__buttons button:hover { .pagination__buttons button:hover {
cursor: pointer; cursor: pointer;
background-color: #fafafa;
} }
.ri-more-line:hover { .selected {
cursor: pointer; color: var(--button-text);
} }
</style> </style>

View File

@ -4,6 +4,8 @@
import ActionButton from "../../../common/ActionButton.svelte" import ActionButton from "../../../common/ActionButton.svelte"
import * as api from "../api" import * as api from "../api"
export let onClosed
let databaseName let databaseName
async function createDatabase() { async function createDatabase() {
@ -14,8 +16,8 @@
</script> </script>
<section> <section>
CREATE A NEW DATABASE FROM HERE Database Name
<input type="text" bind:value={databaseName} /> <input class="uk-input" type="text" bind:value={databaseName} />
<div class="actions"> <div class="actions">
<ActionButton alert on:click={onClosed}>Cancel</ActionButton> <ActionButton alert on:click={onClosed}>Cancel</ActionButton>
<ActionButton disabled={!databaseName} on:click={createDatabase}>Save</ActionButton> <ActionButton disabled={!databaseName} on:click={createDatabase}>Save</ActionButton>

View File

@ -8,12 +8,5 @@
</script> </script>
<section> <section>
<h4 class="budibase__title--4">
<i class="ri-list-settings-line" />
Create / Edit Model
</h4>
<div class="actions">
<ModelView /> <ModelView />
<ActionsHeader />
</div>
</section> </section>

View File

@ -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 { findIndex } from "lodash/fp"
import Modal from "../../../common/Modal.svelte" import Modal from "../../../common/Modal.svelte"
import ActionButton from "../../../common/ActionButton.svelte" import ActionButton from "../../../common/ActionButton.svelte"
import Select from "../../../common/Select.svelte" import Select from "../../../common/Select.svelte"
@ -69,7 +70,10 @@
on:click={async () => { on:click={async () => {
const recordResponse = await api.saveRecord(record || selectedModel, currentAppInfo) const recordResponse = await api.saveRecord(record || selectedModel, currentAppInfo)
backendUiStore.update(state => { backendUiStore.update(state => {
state.selectedView.records.push(recordResponse) const idx = findIndex(state.selectedView.records, {
id: recordResponse.id
})
state.selectedView.records.splice(idx, 1, recordResponse)
return state return state
}) })
onClosed() onClosed()

View File

@ -4,7 +4,6 @@
import { store, backendUiStore } from "../../../builderStore" import { store, backendUiStore } from "../../../builderStore"
import * as api from "../api" import * as api from "../api"
export let modalOpen = false
export let record export let record
$: currentAppInfo = { $: currentAppInfo = {
@ -18,9 +17,14 @@
</script> </script>
<section> <section>
<heading>
<i class="ri-information-line alert" />
<h4 class="budibase__title--4">Delete Record</h4> <h4 class="budibase__title--4">Delete Record</h4>
</heading>
<p>
Are you sure you want to delete this record? All of your data will be Are you sure you want to delete this record? All of your data will be
permanently removed. This action cannot be undone. permanently removed. This action cannot be undone.
</p>
<div class="modal-actions"> <div class="modal-actions">
<ActionButton on:click={onClosed}>Cancel</ActionButton> <ActionButton on:click={onClosed}>Cancel</ActionButton>
<ActionButton <ActionButton
@ -34,3 +38,28 @@
</ActionButton> </ActionButton>
</div> </div>
</section> </section>
<style>
.alert {
color: rgba(255, 0, 31, 1);
background: #fafafa;
padding: 5px;
}
.modal-actions {
position: absolute;
bottom: 0;
background: #fafafa;
border-top: 1px solid #ccc;
width: 100%;
}
heading {
display: flex;
align-items: center;
}
h4 {
margin: 0 0 0 10px;
}
</style>

View File

@ -7,8 +7,9 @@
import Modal from "../common/Modal.svelte" import Modal from "../common/Modal.svelte"
import { map, join, filter, some, find, keys, isDate } from "lodash/fp" import { map, join, filter, some, find, keys, isDate } from "lodash/fp"
import { store } from "../builderStore" import { store } from "../builderStore"
import { common, hierarchy as h } from "../../../core/src" import { common, hierarchy } from "../../../core/src"
import { templateApi, pipe, validate } from "../common/core" import { templateApi, pipe, validate } from "../common/core"
import ActionsHeader from "./ActionsHeader.svelte"
let record let record
let getIndexAllowedRecords let getIndexAllowedRecords
@ -26,7 +27,7 @@
store.subscribe($store => { store.subscribe($store => {
record = $store.currentNode record = $store.currentNode
const flattened = h.getFlattenedHierarchy($store.hierarchy) const flattened = hierarchy.getFlattenedHierarchy($store.hierarchy)
getIndexAllowedRecords = index => getIndexAllowedRecords = index =>
pipe( pipe(
index.allowedRecordNodeIds, index.allowedRecordNodeIds,
@ -78,21 +79,6 @@
return value return value
} }
let getTypeOptions = typeOptions =>
pipe(
typeOptions,
[
keys,
map(
k =>
`<span style="color:var(--slate)">${k}: </span>${getTypeOptionsValueText(
typeOptions[k]
)}`
),
join("<br>"),
]
)
const nameChanged = ev => { const nameChanged = ev => {
const pluralName = n => `${n}s` const pluralName = n => `${n}s`
if (record.collectionName === "") { if (record.collectionName === "") {
@ -102,42 +88,54 @@
</script> </script>
<div class="root"> <div class="root">
<heading>
{#if !editingField}
<i class="ri-list-settings-line button--toggled" />
<h4 class="budibase__title--3">Create / Edit Model</h4>
{:else}
<i class="ri-file-list-line button--toggled" />
<h4 class="budibase__title--3">Create / Edit Field</h4>
{/if}
</heading>
{#if !editingField}
<form class="uk-form-stacked"> <form class="uk-form-stacked">
<h3 class="budibase__label--big">Settings</h3> <h3 class="budibase__label--big">Settings</h3>
<Textbox label="Name" bind:text={record.name} on:change={nameChanged} /> <Textbox label="Name" bind:text={record.name} on:change={nameChanged} />
<div class="horizontal-stack">
{#if !record.isSingle}
<Textbox label="Collection Name" bind:text={record.collectionName} />
{/if}
<div>
<label class="uk-form-label">Parent</label> <label class="uk-form-label">Parent</label>
<div class="uk-form-controls"> <div class="uk-form-controls">
<Select <Select
value={parentRecord} value={parentRecord}
on:change={e => store.newChildRecord()}> on:change={e => {
store.selectExistingNode(record)
store.newChildRecord()
}}>
{#each models as model} {#each models as model}
<option value={model}>{model.name}</option> <option value={model}>{model.name}</option>
{/each} {/each}
</Select> </Select>
</div> </div>
{#if !record.isSingle} </div>
<Textbox label="Collection Name" bind:text={record.collectionName} />
<Textbox
label="Estimated Record Count"
bind:text={record.estimatedRecordCount} />
{/if}
<div class="recordkey">{record.nodeKey()}</div>
</form> </form>
<div class="table-controls"> <div class="table-controls">
<span class="budibase__label--big">Fields</span> <span class="budibase__label--big">Fields</span>
<h4 class="hoverable" on:click={newField}>Add new field</h4> <h4 class="hoverable new-field" on:click={newField}>Add new field</h4>
</div> </div>
<table class="fields-table uk-table budibase__table"> <table class="uk-table fields-table budibase__table">
<thead> <thead>
<tr> <tr>
<th>Edit</th> <th>Edit</th>
<th>Name</th> <th>Name</th>
<th>Type</th> <th>Type</th>
<th>Values</th> <th>Values</th>
<th />
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
@ -151,64 +149,18 @@
</td> </td>
<td>{field.type}</td> <td>{field.type}</td>
<td>{field.typeOptions.values}</td> <td>{field.typeOptions.values}</td>
<td>
<span class="edit-button" on:click={() => deleteField(field)}>
{@html getIcon('trash')}
</span>
</td>
</tr> </tr>
{/each} {/each}
</tbody> </tbody>
</table> </table>
<ActionsHeader />
{#if editingField} {:else}
<Modal
bind:isOpen={editingField}
onClosed={() => onFinishedFieldEdit(false)}>
<h3 class="budibase__title--3">
<i class="ri-file-list-line" />
Create / Edit Field
</h3>
<FieldView <FieldView
field={fieldToEdit} field={fieldToEdit}
onFinished={onFinishedFieldEdit} onFinished={onFinishedFieldEdit}
allFields={record.fields} allFields={record.fields}
store={$store} /> store={$store} />
</Modal>
{/if} {/if}
<!-- <h3 class="budibase__title--3">Indexes</h3> -->
<!-- {#each record.indexes as index}
<div class="index-container">
<div class="index-name">
{index.name}
<span style="margin-left: 7px" on:click={() => editIndex(index)}>
{@html getIcon('edit')}
</span>
</div>
<div class="index-field-row">
<span class="index-label">records indexed:</span>
<span>{getIndexAllowedRecords(index)}</span>
<span class="index-label" style="margin-left: 15px">type:</span>
<span>{index.indexType}</span>
</div>
<div class="index-field-row">
<span class="index-label">map:</span>
<code class="index-mapfilter">{index.map}</code>
</div>
{#if index.filter}
<div class="index-field-row">
<span class="index-label">filter:</span>
<code class="index-mapfilter">{index.filter}</code>
</div>
{/if}
</div>
{:else}
<div class="no-indexes">No indexes added.</div>
{/each} -->
</div> </div>
<style> <style>
@ -217,10 +169,15 @@
padding: 2rem; padding: 2rem;
} }
.recordkey { .horizontal-stack {
font-size: 14px; display: flex;
font-weight: 600; justify-content: space-between
color: var(--primary100); }
.new-field {
font-size: 16px;
font-weight: bold;
color: var(--button-text);
} }
.fields-table { .fields-table {
@ -228,24 +185,10 @@
border-collapse: collapse; border-collapse: collapse;
} }
.edit-button {
cursor: pointer;
color: var(--secondary25);
}
.edit-button:hover {
cursor: pointer;
color: var(--secondary75);
}
tbody > tr:hover { tbody > tr:hover {
background-color: var(--primary10); background-color: var(--primary10);
} }
tbody > tr:hover .edit-button {
color: var(--secondary75);
}
.table-controls { .table-controls {
display: flex; display: flex;
justify-content: space-between; justify-content: space-between;
@ -256,7 +199,12 @@
cursor: pointer; cursor: pointer;
} }
heading {
display: flex;
align-items: center;
}
h4 { h4 {
margin: 0; margin: 0 0 0 10px;
} }
</style> </style>

View File

@ -9,9 +9,6 @@
export let type export let type
let navActive = "" let navActive = ""
let expanded = false
$: hasChildren = (node.children || node.indexes) && (node.children.length || node.indexes.length)
const ICON_MAP = { const ICON_MAP = {
index: "ri-eye-line", index: "ri-eye-line",
@ -38,16 +35,9 @@
class:capitalized={type === 'record'} class:capitalized={type === 'record'}
style="padding-left: {20 + level * 20}px" style="padding-left: {20 + level * 20}px"
class:selected={navActive}> class:selected={navActive}>
{#if hasChildren}
<i
class={`ri-arrow-${expanded ? "down" : "right"}-s-line`}
on:click={() => expanded = !expanded}
/>
{/if}
<i class={ICON_MAP[type]} /> <i class={ICON_MAP[type]} />
<span style="margin-left: 1rem">{node.name}</span> <span style="margin-left: 1rem">{node.name}</span>
</div> </div>
{#if expanded}
{#if node.children} {#if node.children}
{#each node.children as child} {#each node.children as child}
<svelte:self node={child} level={level + 1} type="record" /> <svelte:self node={child} level={level + 1} type="record" />
@ -58,7 +48,6 @@
<svelte:self node={index} level={level + 1} type="index" /> <svelte:self node={index} level={level + 1} type="index" />
{/each} {/each}
{/if} {/if}
{/if}
</div> </div>
<style> <style>