improvements to linked records

This commit is contained in:
Martin McKeaveney 2020-06-23 13:50:45 +01:00
parent eca7b69949
commit 8ab2e0b6aa
8 changed files with 131 additions and 211 deletions

View File

@ -58,7 +58,7 @@ export const getBackendUiStore = () => {
state.tabs.SETUP_PANEL = "SETUP"
return state
}),
save: async ({ instanceId, model }) => {
save: async ({ model }) => {
const updatedModel = cloneDeep(model)
// TODO: refactor
@ -70,7 +70,7 @@ export const getBackendUiStore = () => {
}
}
const SAVE_MODEL_URL = `/api/${instanceId}/models`
const SAVE_MODEL_URL = `/api/models`
const response = await api.post(SAVE_MODEL_URL, updatedModel)
const savedModel = await response.json()
@ -86,6 +86,8 @@ export const getBackendUiStore = () => {
state.models = state.models
}
// TODO: fetch models
store.actions.models.select(savedModel)
return state
})

View File

@ -9,8 +9,13 @@
let records = []
let linkedRecords = new Set(linked)
$: linked = [...linkedRecords]
$: FIELDS_TO_HIDE = ["modelId", "type", "_id", "_rev", $backendUiStore.selectedModel.name]
async function fetchRecords() {
const FETCH_RECORDS_URL = `/api/${$backendUiStore.selectedDatabase._id}/${modelId}/records`
const FETCH_RECORDS_URL = `/api/${modelId}/records`
const response = await api.get(FETCH_RECORDS_URL)
records = await response.json()
}
@ -19,17 +24,25 @@
fetchRecords()
})
function linkRecord(record) {
linked.push(record._id)
function linkRecord(id) {
if (linkedRecords.has(id)) {
linkedRecords.delete(id);
} else {
linkedRecords.add(id)
}
linkedRecords = linkedRecords
}
</script>
<section>
<h3>{linkName}</h3>
<header>
<h3>{linkName}</h3>
</header>
{#each records as record}
<div class="linked-record" on:click={() => linkRecord(record)}>
<div class="fields">
{#each Object.keys(record).slice(0, 2) as key}
<div class="linked-record" on:click={() => linkRecord(record._id)}>
<div class="fields" class:selected={linkedRecords.has(record._id)}>
{#each Object.keys(record).filter(key => !FIELDS_TO_HIDE.includes(key)) as key}
<div class="field">
<span>{key}</span>
<p>{record[key]}</p>
@ -41,8 +54,15 @@
</section>
<style>
.fields.selected {
background: var(--light-grey);
}
h3 {
font-size: 20px;
font-size: 18px;
font-weight: 600;
margin-bottom: 12px;
color: var(--ink);
}
.fields {
@ -50,12 +70,14 @@
display: grid;
grid-template-columns: 1fr 1fr 1fr;
grid-gap: 20px;
background: var(--grey);
background: var(--white);
border: 1px solid var(--grey);
border-radius: 5px;
transition: 0.5s all;
margin-bottom: 8px;
}
.field:hover {
.fields:hover {
cursor: pointer;
}

View File

@ -4,21 +4,27 @@
import { backendUiStore } from "builderStore"
import api from "builderStore/api"
export let ids = []
export let header
let records = []
let open = false
$: FIELDS_TO_HIDE = ["modelId", "type", "_id", "_rev", $backendUiStore.selectedModel.name]
async function fetchRecords() {
const FETCH_RECORDS_URL = `/api/${$backendUiStore.selectedDatabase._id}/records/search`
const response = await api.post(FETCH_RECORDS_URL, {
keys: ids,
const response = await api.post("/api/records/search", {
keys: ids
})
records = await response.json()
}
$: ids && fetchRecords()
$: ids && fetchRecords()
function toggleOpen() {
open = !open
}
onMount(() => {
fetchRecords()
@ -26,14 +32,17 @@
</script>
<section>
<a on:click={() => (open = !open)}>{records.length}</a>
<a on:click={toggleOpen}>{records.length}</a>
{#if open}
<div class="popover" transition:fade>
<header>
<h3>{header}</h3>
<i class="ri-close-circle-fill" on:click={toggleOpen} />
</header>
{#each records as record}
<div class="linked-record">
<div class="fields">
{#each Object.keys(record).slice(0, 2) as key}
{#each Object.keys(record).filter(key => !FIELDS_TO_HIDE.includes(key)) as key}
<div class="field">
<span>{key}</span>
<p>{record[key]}</p>
@ -47,11 +56,29 @@
</section>
<style>
header {
width: 100%;
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 12px;
}
i {
font-size: 24px;
color: var(--ink-lighter);
}
i:hover {
cursor: pointer;
}
a {
font-size: 14px;
}
.popover {
width: 500px;
position: absolute;
right: 15%;
padding: 20px;
@ -61,6 +88,8 @@
h3 {
font-size: 20px;
font-weight: bold;
margin: 0;
}
.fields {
@ -71,10 +100,7 @@
background: var(--white);
border: 1px solid var(--grey);
border-radius: 5px;
}
.field:hover {
cursor: pointer;
margin-bottom: 8px;
}
.field span {

View File

@ -3,7 +3,7 @@
import { store, backendUiStore } from "builderStore"
import { notifier } from "@beyonk/svelte-notifications"
import { compose, map, get, flatten } from "lodash/fp"
import ActionButton from "components/common/ActionButton.svelte"
import { Button } from "@budibase/bbui"
import LinkedRecordSelector from "components/common/LinkedRecordSelector.svelte"
import Select from "components/common/Select.svelte"
import RecordFieldControl from "./RecordFieldControl.svelte"
@ -68,13 +68,19 @@
</script>
<div class="actions">
<h4 class="budibase__title--4">Create / Edit Record</h4>
<header>
<i class="ri-file-user-fill" />
<h4 class="budibase__title--4">Create / Edit Record</h4>
</header>
<ErrorsBox {errors} />
<form on:submit|preventDefault class="uk-form-stacked">
{#each modelSchema as [key, meta]}
<div class="uk-margin">
{#if meta.type === 'link'}
<LinkedRecordSelector bind:linked={record[key]} linkName={key} modelId={meta.modelId} />
<LinkedRecordSelector
bind:linked={record[key]}
linkName={key}
modelId={meta.modelId} />
{:else}
<RecordFieldControl
type={determineInputType(meta)}
@ -87,14 +93,44 @@
</form>
</div>
<footer>
<ActionButton alert on:click={onClosed}>Cancel</ActionButton>
<ActionButton on:click={saveRecord}>Save</ActionButton>
<Button secondary on:click={onClosed}>Cancel</Button>
<Button attention on:click={saveRecord}>Save</Button>
</footer>
<style>
header {
margin-bottom: 40px;
display: grid;
grid-gap: 5px;
grid-template-columns: 40px 1fr;
align-items: center;
}
i {
height: 40px;
width: 40px;
display: flex;
align-items: center;
justify-content: center;
background: var(--secondary);
color: var(--ink);
font-size: 20px;
margin-right: 20px;
border-radius: 3px;
}
h4 {
display: inline-block;
font-size: 24px;
font-weight: bold;
color: var(--ink);
margin: 0;
}
.actions {
padding: 30px;
}
footer {
padding: 20px;
background: #fafafa;

View File

@ -51,3 +51,16 @@
on:input={handleInput}
on:change={handleInput} />
{/if}
<style>
label {
display: block;
font-size: 18px;
font-weight: 500;
margin-bottom: 12px;
}
input {
color: var(--dark-grey);
}
</style>

View File

@ -40,7 +40,7 @@
return
}
const DELETE_MODEL_URL = `/api/${instanceId}/models/${model._id}/${model._rev}`
const DELETE_MODEL_URL = `/api/models/${model._id}/${model._rev}`
const response = await api.delete(DELETE_MODEL_URL)
backendUiStore.update(state => {
state.selectedView = null

View File

@ -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/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>

View File

@ -85,42 +85,10 @@
dimensions = {top, left}
}
})
function openColorpicker(event) {
if (colorPreview) {
const {
top: spaceAbove,
width,
bottom,
right,
left: spaceLeft,
} = colorPreview.getBoundingClientRect()
const { innerHeight, innerWidth } = window
const { offsetLeft, offsetTop } = colorPreview
//get the scrollTop value for all scrollable parent elements
let scrollTop = parentNodes.reduce(
(scrollAcc, el) => (scrollAcc += el.scrollTop),
0
)
const spaceBelow = innerHeight - spaceAbove - previewHeight
const top =
spaceAbove > spaceBelow
? offsetTop - pickerHeight - scrollTop
: offsetTop + previewHeight - scrollTop
//TOO: Testing and Scroll Awareness for x Scroll
const spaceRight = innerWidth - spaceLeft + previewWidth
const left =
spaceRight > spaceLeft
? offsetLeft + previewWidth + pickerWidth
: offsetLeft - pickerWidth
dimensions = { top, left }
open = true
function onColorChange(color) {
value = color.detail;
dispatch("change", color.detail)
}
</script>
@ -142,13 +110,9 @@
<span>&times;</span>
</div>
{/if}
{:else}
<div class="color-preview preview-error" style={errorPreviewStyle}>
<span>&times;</span>
</div>
{/if}
</div>
<style>
.color-preview-container{
display: flex;