budibase/packages/builder/src/components/database/ModelDataTable/ModelDataTable.svelte

251 lines
5.7 KiB
Svelte
Raw Normal View History

2020-03-10 14:53:23 +01:00
<script>
2020-04-06 16:38:33 +02:00
import { onMount, getContext } from "svelte"
import { store, backendUiStore } from "builderStore"
2020-03-25 17:59:32 +01:00
import {
tap,
get,
find,
last,
compose,
flatten,
map,
remove,
2020-03-27 17:58:32 +01:00
keys,
takeRight,
2020-03-25 17:59:32 +01:00
} from "lodash/fp"
import Select from "components/common/Select.svelte"
import { getIndexSchema } from "components/common/core"
import ActionButton from "components/common/ActionButton.svelte"
2020-03-12 15:23:29 +01:00
import TablePagination from "./TablePagination.svelte"
2020-04-06 16:38:33 +02:00
import { DeleteRecordModal, CreateEditRecordModal } from "./modals"
2020-03-12 15:23:29 +01:00
import * as api from "./api"
2020-03-10 17:06:30 +01:00
2020-04-06 16:38:33 +02:00
const { open, close } = getContext("simple-modal")
const editRecord = async row => {
open(
CreateEditRecordModal,
{
onClosed: close,
record: await selectRecord(row),
},
{ styleContent: { padding: "0" } }
)
}
2020-04-06 16:44:53 +02:00
const deleteRecord = async row => {
open(
DeleteRecordModal,
{
onClosed: close,
record: await selectRecord(row),
},
{ styleContent: { padding: "0" } }
)
}
2020-04-06 16:38:33 +02:00
async function selectRecord(record) {
return await api.loadRecord(record.key, {
appname: $store.appname,
instanceId: $backendUiStore.selectedDatabase.id,
})
}
2020-03-12 15:23:29 +01:00
const ITEMS_PER_PAGE = 10
2020-03-25 17:59:32 +01:00
// Internal headers we want to hide from the user
2020-03-27 17:58:32 +01:00
const INTERNAL_HEADERS = ["key", "sortKey", "type", "id", "isNew"]
2020-03-22 10:21:18 +01:00
2020-03-12 15:23:29 +01:00
let modalOpen = false
2020-03-24 12:35:46 +01:00
let data = []
let headers = []
2020-03-24 12:35:46 +01:00
let views = []
2020-03-22 10:21:18 +01:00
let currentPage = 0
2020-03-12 15:23:29 +01:00
2020-03-24 12:35:46 +01:00
$: views = $backendUiStore.selectedRecord
? childViewsForRecord($store.hierarchy)
: $store.hierarchy.indexes
$: currentAppInfo = {
appname: $store.appname,
2020-03-23 11:43:34 +01:00
instanceId: $backendUiStore.selectedDatabase.id,
}
2020-03-24 12:35:46 +01:00
$: fetchRecordsForView(
$backendUiStore.selectedView,
$backendUiStore.selectedDatabase
).then(records => {
2020-03-25 11:59:47 +01:00
data = records || []
2020-03-25 17:59:32 +01:00
headers = hideInternalHeaders($backendUiStore.selectedView)
2020-03-24 12:35:46 +01:00
})
2020-03-25 11:59:47 +01:00
$: paginatedData = data.slice(
currentPage * ITEMS_PER_PAGE,
currentPage * ITEMS_PER_PAGE + ITEMS_PER_PAGE
)
2020-03-25 17:59:32 +01:00
const getSchema = getIndexSchema($store.hierarchy)
2020-04-06 16:38:33 +02:00
const childViewsForRecord = compose(flatten, map("indexes"), get("children"))
2020-03-12 15:23:29 +01:00
2020-03-25 17:59:32 +01:00
const hideInternalHeaders = compose(
remove(headerName => INTERNAL_HEADERS.includes(headerName)),
map(get("name")),
getSchema
)
2020-03-24 12:35:46 +01:00
async function fetchRecordsForView(view, instance) {
if (!view || !view.name) return
2020-03-25 11:59:47 +01:00
2020-03-24 12:35:46 +01:00
const viewName = $backendUiStore.selectedRecord
2020-03-26 15:31:56 +01:00
? `${$backendUiStore.selectedRecord.key}/${view.name}`
2020-03-24 12:35:46 +01:00
: view.name
2020-03-25 11:59:47 +01:00
return await api.fetchDataForView(viewName, {
2020-03-24 12:35:46 +01:00
appname: $store.appname,
instanceId: instance.id,
})
}
2020-03-23 15:15:09 +01:00
function drillIntoRecord(record) {
backendUiStore.update(state => {
2020-03-30 12:57:22 +02:00
state.breadcrumbs = [...state.breadcrumbs, record.type, record.id]
2020-03-24 12:35:46 +01:00
state.selectedRecord = record
2020-03-25 17:59:32 +01:00
state.selectedView = childViewsForRecord($store.hierarchy)[0]
2020-03-23 15:15:09 +01:00
return state
})
}
2020-03-25 11:59:47 +01:00
onMount(() => {
if (views.length) {
backendUiStore.actions.views.select(views[0])
}
})
2020-03-10 14:53:23 +01:00
</script>
2020-03-23 11:43:34 +01:00
<section>
<div class="table-controls">
<h2 class="title">
{takeRight(2, $backendUiStore.breadcrumbs).join(' / ')}
</h2>
2020-03-25 11:59:47 +01:00
<Select icon="ri-eye-line" bind:value={$backendUiStore.selectedView}>
2020-03-23 11:43:34 +01:00
{#each views as view}
2020-03-25 11:59:47 +01:00
<option value={view}>{view.name}</option>
2020-03-23 11:43:34 +01:00
{/each}
</Select>
</div>
<table class="uk-table">
<thead>
<tr>
<th>Edit</th>
{#each headers as header}
<th>{header}</th>
2020-03-10 17:06:30 +01:00
{/each}
2020-03-23 11:43:34 +01:00
</tr>
</thead>
<tbody>
2020-03-25 11:59:47 +01:00
{#if paginatedData.length === 0}
2020-03-23 11:43:34 +01:00
<div class="no-data">No Data.</div>
{/if}
2020-03-25 11:59:47 +01:00
{#each paginatedData as row}
2020-03-23 11:43:34 +01:00
<tr class="hoverable">
<td>
<div class="uk-inline">
<i class="ri-more-line" />
<div uk-dropdown="mode: click">
<ul class="uk-nav uk-dropdown-nav">
<li>
2020-03-23 15:15:09 +01:00
<div on:click={() => drillIntoRecord(row)}>View</div>
2020-03-23 11:43:34 +01:00
</li>
<li
on:click={() => {
2020-04-06 16:38:33 +02:00
editRecord(row)
2020-03-23 11:43:34 +01:00
}}>
<div>Edit</div>
</li>
<li>
<div
on:click={() => {
2020-04-06 16:44:53 +02:00
deleteRecord(row)
}}>
2020-03-23 11:43:34 +01:00
Delete
</div>
</li>
</ul>
2020-03-12 15:23:29 +01:00
</div>
2020-03-23 11:43:34 +01:00
</div>
</td>
{#each headers as header}
<td>{row[header]}</td>
{/each}
</tr>
{/each}
</tbody>
</table>
<TablePagination
2020-03-25 11:59:47 +01:00
{data}
2020-03-23 11:43:34 +01:00
bind:currentPage
pageItemCount={data.length}
2020-03-25 17:59:32 +01:00
{ITEMS_PER_PAGE} />
2020-03-23 11:43:34 +01:00
</section>
2020-03-10 14:53:23 +01:00
<style>
.title {
font-size: 24px;
font-weight: 600;
text-rendering: optimizeLegibility;
text-transform: capitalize;
}
.select {
background: white;
}
2020-03-10 14:53:23 +01:00
table {
border: 1px solid #ccc;
2020-03-10 17:06:30 +01:00
background: #fff;
2020-03-21 21:39:37 +01:00
border-radius: 3px;
2020-03-22 10:21:18 +01:00
border-collapse: collapse;
2020-03-10 14:53:23 +01:00
}
thead {
background: #f9f9f9;
border: 1px solid #ccc;
2020-03-10 14:53:23 +01:00
}
thead th {
color: var(--button-text);
text-transform: capitalize;
2020-03-10 17:06:30 +01:00
font-weight: 500;
2020-04-06 16:38:33 +02:00
font-size: 14px;
text-rendering: optimizeLegibility;
letter-spacing: 1px;
2020-03-10 14:53:23 +01:00
}
2020-03-10 17:06:30 +01:00
tbody tr {
2020-03-10 14:53:23 +01:00
border-bottom: 1px solid #ccc;
transition: 0.3s background-color;
color: var(--secondary100);
font-size: 14px;
}
tbody tr:hover {
background: #fafafa;
2020-03-10 14:53:23 +01:00
}
2020-03-10 17:06:30 +01:00
.table-controls {
display: flex;
justify-content: space-between;
align-items: baseline;
margin-top: 10px;
2020-03-10 17:06:30 +01:00
}
2020-03-12 15:23:29 +01:00
.ri-more-line:hover,
.uk-dropdown-nav li:hover {
2020-03-12 15:23:29 +01:00
cursor: pointer;
}
2020-03-22 10:21:18 +01:00
.no-data {
padding: 20px;
}
2020-03-10 17:06:30 +01:00
</style>