Add functional backend UI for navigating relationships

This commit is contained in:
Andrew Kingston 2020-10-01 11:01:06 +01:00
parent 9a9430e0cf
commit 7aa040d314
5 changed files with 118 additions and 79 deletions

View File

@ -0,0 +1,32 @@
<script>
import api from "builderStore/api"
import Table from "./Table.svelte"
import { onMount } from "svelte"
import { backendUiStore } from "builderStore"
export let modelId
export let recordId
export let fieldName
let record
$: data = record?.[fieldName] ?? []
$: linkedModelId = data?.length ? data[0].modelId : null
$: linkedModel = $backendUiStore.models.find(
model => model._id === linkedModelId
)
$: schema = linkedModel?.schema
$: model = $backendUiStore.models.find(model => model._id === modelId)
$: title = `${record?.[model?.primaryDisplay]} - ${fieldName}`
$: fetchData(modelId, recordId)
async function fetchData(modelId, recordId) {
const QUERY_VIEW_URL = `/api/${modelId}/${recordId}/enrich`
const response = await api.get(QUERY_VIEW_URL)
record = await response.json()
}
</script>
{#if record && record._id === recordId}
<Table {title} {schema} {data} />
{/if}

View File

@ -1,5 +1,5 @@
<script> <script>
import { goto } from "@sveltech/routify" import { goto, params } from "@sveltech/routify"
import { onMount } from "svelte" import { onMount } from "svelte"
import fsort from "fast-sort" import fsort from "fast-sort"
import getOr from "lodash/fp/getOr" import getOr from "lodash/fp/getOr"
@ -32,13 +32,17 @@
$: paginatedData = $: paginatedData =
sorted && sorted.length sorted && sorted.length
? sorted.slice( ? sorted.slice(
currentPage * ITEMS_PER_PAGE, currentPage * ITEMS_PER_PAGE,
currentPage * ITEMS_PER_PAGE + ITEMS_PER_PAGE currentPage * ITEMS_PER_PAGE + ITEMS_PER_PAGE,
) )
: [] : []
$: modelId = data?.length ? data[0].modelId : null
function selectRelationship(recordId, fieldName) { function selectRelationship(record, fieldName) {
$goto(`../relationship/${recordId}/${fieldName}`) if (!record?.[fieldName]?.length) {
return
}
$goto(`/${$params.application}/backend/model/${modelId}/relationship/${record._id}/${fieldName}`)
} }
</script> </script>
@ -46,66 +50,68 @@
<div class="table-controls"> <div class="table-controls">
<h2 class="title">{title}</h2> <h2 class="title">{title}</h2>
<div class="popovers"> <div class="popovers">
<slot /> <slot/>
</div> </div>
</div> </div>
<table class="bb-table"> <table class="bb-table">
<thead> <thead>
<tr> <tr>
{#if allowEditing} {#if allowEditing}
<th class="edit-header"> <th class="edit-header">
<div>Edit</div> <div>Edit</div>
</th> </th>
{/if} {/if}
{#each columns as header} {#each columns as header}
<th> <th>
{#if allowEditing} {#if allowEditing}
<ColumnHeaderPopover field={schema[header]} /> <ColumnHeaderPopover field={schema[header]}/>
{:else}{header}{/if} {:else}
</th> <div class="header">{header}</div>
{/each} {/if}
</tr> </th>
{/each}
</tr>
</thead> </thead>
<tbody> <tbody>
{#if paginatedData.length === 0} {#if paginatedData.length === 0}
{#if allowEditing}
<td class="no-border">No data.</td>
{/if}
{#each columns as header, idx}
<td class="no-border">
{#if idx === 0}No data.{/if}
</td>
{/each}
{/if}
{#each paginatedData as row}
<tr>
{#if allowEditing} {#if allowEditing}
<td class="no-border">No data.</td> <td>
<EditRowPopover {row}/>
</td>
{/if} {/if}
{#each columns as header, idx} {#each columns as header}
<td class="no-border"> <td>
{#if idx === 0}No data.{/if} {#if schema[header].type === 'link'}
<div
class:link={row[header] && row[header].length}
on:click={() => selectRelationship(row, header)}>
{row[header] ? row[header].length : 0} linked row(s)
</div>
{:else if schema[header].type === 'attachment'}
<AttachmentList files={row[header] || []}/>
{:else}{getOr('', header, row)}{/if}
</td> </td>
{/each} {/each}
{/if} </tr>
{#each paginatedData as row} {/each}
<tr>
{#if allowEditing}
<td>
<EditRowPopover {row} />
</td>
{/if}
{#each columns as header}
<td>
{#if schema[header].type === 'link'}
<div
class="link"
on:click={() => selectRelationship(row._id, header)}>
{row[header] ? row[header].length : 0} linked row(s)
</div>
{:else if schema[header].type === 'attachment'}
<AttachmentList files={row[header] || []} />
{:else}{getOr('', header, row)}{/if}
</td>
{/each}
</tr>
{/each}
</tbody> </tbody>
</table> </table>
<TablePagination <TablePagination
{data} {data}
bind:currentPage bind:currentPage
pageItemCount={paginatedData.length} pageItemCount={paginatedData.length}
{ITEMS_PER_PAGE} /> {ITEMS_PER_PAGE}/>
</section> </section>
<style> <style>
@ -121,12 +127,14 @@
border: 1px solid var(--grey-4); border: 1px solid var(--grey-4);
background: #fff; background: #fff;
border-collapse: collapse; border-collapse: collapse;
margin-top: 0;
} }
thead { thead {
background: var(--grey-3); background: var(--grey-3);
border: 1px solid var(--grey-4); border: 1px solid var(--grey-4);
} }
thead th { thead th {
color: var(--ink); color: var(--ink);
font-weight: 500; font-weight: 500;
@ -138,11 +146,16 @@
padding-top: 0; padding-top: 0;
padding-bottom: 0; padding-bottom: 0;
} }
thead th:hover { thead th:hover {
color: var(--blue); color: var(--blue);
cursor: pointer; cursor: pointer;
} }
.header {
text-transform: capitalize;
}
td { td {
max-width: 200px; max-width: 200px;
text-overflow: ellipsis; text-overflow: ellipsis;
@ -152,6 +165,7 @@
padding: var(--spacing-l) var(--spacing-m); padding: var(--spacing-l) var(--spacing-m);
font-size: var(--font-size-xs); font-size: var(--font-size-xs);
} }
td.no-border { td.no-border {
border: none; border: none;
} }
@ -161,6 +175,7 @@
transition: 0.3s background-color; transition: 0.3s background-color;
color: var(--ink); color: var(--ink);
} }
tbody tr:hover { tbody tr:hover {
background: var(--grey-1); background: var(--grey-1);
} }
@ -175,11 +190,13 @@
:global(.popovers > div) { :global(.popovers > div) {
margin-right: var(--spacing-m); margin-right: var(--spacing-m);
margin-bottom: var(--spacing-xl);
} }
.edit-header { .edit-header {
width: 60px; width: 60px;
} }
.edit-header:hover { .edit-header:hover {
cursor: default; cursor: default;
color: var(--ink); color: var(--ink);
@ -188,6 +205,7 @@
.link { .link {
text-decoration: underline; text-decoration: underline;
} }
.link:hover { .link:hover {
color: var(--grey-6); color: var(--grey-6);
cursor: pointer; cursor: pointer;

View File

@ -69,11 +69,13 @@
{/each} {/each}
</Select> </Select>
<Toggle {#if field.type !== 'link'}
checked={!field.constraints.presence.allowEmpty} <Toggle
on:change={e => (field.constraints.presence.allowEmpty = !e.target.checked)} checked={!field.constraints.presence.allowEmpty}
thin on:change={e => (field.constraints.presence.allowEmpty = !e.target.checked)}
text="Required" /> thin
text="Required" />
{/if}
{#if field.type === 'string'} {#if field.type === 'string'}
<Input <Input

View File

@ -13,19 +13,17 @@
export let onClosed export let onClosed
let errors = [] let errors = []
let selectedModel $: model = record.modelId ? $backendUiStore.models.find(model => model._id === record?.modelId) : $backendUiStore.selectedModel
$: modelSchema = Object.entries(model?.schema ?? {})
$: modelSchema = $backendUiStore.selectedModel $: console.log(modelSchema)
? Object.entries($backendUiStore.selectedModel.schema)
: []
async function saveRecord() { async function saveRecord() {
const recordResponse = await api.saveRecord( const recordResponse = await api.saveRecord(
{ {
...record, ...record,
modelId: $backendUiStore.selectedModel._id, modelId: model._id,
}, },
$backendUiStore.selectedModel._id model._id
) )
if (recordResponse.errors) { if (recordResponse.errors) {
errors = Object.keys(recordResponse.errors) errors = Object.keys(recordResponse.errors)

View File

@ -1,18 +1,7 @@
<script> <script>
import { onMount } from "svelte"
import { params } from "@sveltech/routify" import { params } from "@sveltech/routify"
import { backendUiStore } from "builderStore" import RelationshipDataTable from "components/backend/DataTable/RelationshipDataTable.svelte"
import api from "../../../../../../../../builderStore/api"
console.log($params)
let data
onMount(async () => {
const QUERY_VIEW_URL = `/api/${$params.selectedModel}/${$params.selectedRecord}/enrich`
const response = await api.get(QUERY_VIEW_URL)
data = await response.json()
console.log(data)
})
</script> </script>
<div>hello world!</div> <RelationshipDataTable modelId={$params.selectedModel} recordId={$params.selectedRecord}
fieldName={$params.selectedField}/>