group by complete
This commit is contained in:
parent
28b4b6fcb9
commit
d08db301af
|
@ -93,10 +93,8 @@ export const getBackendUiStore = () => {
|
|||
// delete the original if renaming
|
||||
delete state.draftModel.schema[originalName]
|
||||
|
||||
state.draftModel.schema = {
|
||||
...state.draftModel.schema,
|
||||
[field.name]: cloneDeep(field),
|
||||
}
|
||||
state.draftModel.schema[field.name] = cloneDeep(field)
|
||||
|
||||
store.actions.models.save(state.draftModel)
|
||||
return state
|
||||
})
|
||||
|
@ -126,8 +124,15 @@ export const getBackendUiStore = () => {
|
|||
},
|
||||
save: async view => {
|
||||
await api.post(`/api/views`, view)
|
||||
|
||||
store.update(state => {
|
||||
state.selectedModel.views[view.name] = view
|
||||
const viewModel = state.models.find(model => model._id === view.modelId)
|
||||
// TODO: Cleaner?
|
||||
if (!viewModel.views) viewModel.views = {}
|
||||
if (view.originalName) delete viewModel.views[view.originalName]
|
||||
viewModel.views[view.name] = view
|
||||
|
||||
state.models = state.models
|
||||
state.selectedView = view
|
||||
return state
|
||||
})
|
||||
|
|
|
@ -17,47 +17,62 @@
|
|||
import ColumnHeaderPopover from "./popovers/ColumnHeader.svelte"
|
||||
import EditRowPopover from "./popovers/EditRow.svelte"
|
||||
import CalculationPopover from "./popovers/Calculate.svelte"
|
||||
import GroupByPopover from "./popovers/GroupBy.svelte"
|
||||
|
||||
const COLUMNS = [
|
||||
let COLUMNS = [
|
||||
{
|
||||
name: "Group",
|
||||
key: "key",
|
||||
},
|
||||
{
|
||||
name: "sum",
|
||||
key: "value.sum",
|
||||
},
|
||||
{
|
||||
name: "min",
|
||||
key: "value.min",
|
||||
key: "value.min"
|
||||
},
|
||||
{
|
||||
name: "max",
|
||||
key: "value.max",
|
||||
key: "value.max"
|
||||
},
|
||||
{
|
||||
name: "sumsqr",
|
||||
key: "value.sumsqr",
|
||||
key: "value.sumsqr"
|
||||
},
|
||||
{
|
||||
name: "count",
|
||||
key: "value.count",
|
||||
key: "value.count"
|
||||
},
|
||||
{
|
||||
name: "avg",
|
||||
key: "value.avg",
|
||||
},
|
||||
key: "value.avg"
|
||||
}
|
||||
]
|
||||
|
||||
export let view = {}
|
||||
|
||||
let data = []
|
||||
|
||||
$: selectedView = $backendUiStore.selectedView
|
||||
$: !selectedView.name.startsWith("all_") && fetchViewData(selectedView)
|
||||
$: viewName = view.name
|
||||
$: !viewName.startsWith("all_") && fetchViewData(viewName)
|
||||
|
||||
async function fetchViewData() {
|
||||
const QUERY_VIEW_URL = `/api/views/${$backendUiStore.selectedView.name}?stats=true`
|
||||
async function fetchViewData(viewName) {
|
||||
let QUERY_VIEW_URL = `/api/views/${viewName}?stats=true`
|
||||
if (view.groupBy) {
|
||||
QUERY_VIEW_URL += `&group=${view.groupBy}`
|
||||
}
|
||||
|
||||
const response = await api.get(QUERY_VIEW_URL)
|
||||
data = await response.json()
|
||||
}
|
||||
</script>
|
||||
|
||||
<Table title={decodeURI(selectedView.name)} columns={COLUMNS} {data}>
|
||||
<CalculationPopover view={selectedView} />
|
||||
</Table>
|
||||
<Table
|
||||
title={decodeURI(view.name)}
|
||||
columns={COLUMNS}
|
||||
{data}
|
||||
>
|
||||
<CalculationPopover {view} />
|
||||
<GroupByPopover {view} />
|
||||
</Table>
|
|
@ -4,7 +4,7 @@
|
|||
import { store, backendUiStore } from "builderStore"
|
||||
import * as api from "../api"
|
||||
|
||||
export let view
|
||||
export let viewName
|
||||
export let onClosed
|
||||
</script>
|
||||
|
||||
|
@ -24,7 +24,7 @@
|
|||
<ActionButton
|
||||
alert
|
||||
on:click={async () => {
|
||||
await backendUiStore.actions.views.delete(view)
|
||||
await backendUiStore.actions.views.delete(viewName)
|
||||
notifier.danger('View deleted')
|
||||
onClosed()
|
||||
}}>
|
||||
|
|
|
@ -0,0 +1,80 @@
|
|||
<script>
|
||||
import { Popover, Button, Icon, Input, Select } from "@budibase/bbui"
|
||||
import { backendUiStore } from "builderStore"
|
||||
import { notifier } from "builderStore/store/notifications"
|
||||
import CreateEditRecord from "../modals/CreateEditRecord.svelte"
|
||||
|
||||
const CALCULATIONS = [
|
||||
{
|
||||
name: "Statistics",
|
||||
key: "stats",
|
||||
},
|
||||
]
|
||||
|
||||
export let view = {}
|
||||
|
||||
let anchor
|
||||
let dropdown
|
||||
|
||||
$: viewModel = $backendUiStore.models.find(
|
||||
({ _id }) => _id === $backendUiStore.selectedView.modelId
|
||||
)
|
||||
$: fields =
|
||||
viewModel && Object.keys(viewModel.schema)
|
||||
|
||||
function saveView() {
|
||||
backendUiStore.actions.views.save(view)
|
||||
notifier.success(`View ${view.name} saved.`)
|
||||
dropdown.hide()
|
||||
}
|
||||
</script>
|
||||
|
||||
<div bind:this={anchor}>
|
||||
<Button text small on:click={dropdown.show}>
|
||||
<Icon name="calculate" />
|
||||
Group By
|
||||
</Button>
|
||||
</div>
|
||||
<Popover bind:this={dropdown} {anchor} align="left">
|
||||
<h5>Group By</h5>
|
||||
<div class="input-group-row">
|
||||
<p>Group By</p>
|
||||
<Select secondary thin bind:value={view.groupBy}>
|
||||
<option value={false}>None</option>
|
||||
{#each fields as field}
|
||||
<option value={field}>{field}</option>
|
||||
{/each}
|
||||
</Select>
|
||||
</div>
|
||||
<div class="button-group">
|
||||
<Button secondary on:click={dropdown.hide}>Cancel</Button>
|
||||
<Button primary on:click={saveView}>Save</Button>
|
||||
</div>
|
||||
</Popover>
|
||||
|
||||
<style>
|
||||
h5 {
|
||||
margin-bottom: var(--spacing-l);
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.button-group {
|
||||
margin-top: var(--spacing-l);
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
gap: var(--spacing-s);
|
||||
}
|
||||
|
||||
.input-group-row {
|
||||
display: grid;
|
||||
grid-template-columns: 50px 1fr 20px 1fr;
|
||||
gap: var(--spacing-s);
|
||||
margin-bottom: var(--spacing-l);
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
p {
|
||||
margin: 0;
|
||||
font-size: var(--font-size-xs);
|
||||
}
|
||||
</style>
|
|
@ -28,7 +28,7 @@
|
|||
open(
|
||||
DeleteTableModal,
|
||||
{
|
||||
onClosed: hideEditor,
|
||||
onClosed: close,
|
||||
table,
|
||||
},
|
||||
{ styleContent: { padding: "0" } }
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
<script>
|
||||
import { getContext } from "svelte"
|
||||
import { backendUiStore } from "builderStore"
|
||||
import { notifier } from "builderStore/store/notifications"
|
||||
import { DropdownMenu, Button, Icon, Input, Select } from "@budibase/bbui"
|
||||
import { FIELDS } from "constants/backend"
|
||||
import DeleteViewModal from "components/database/DataTable/modals/DeleteView.svelte"
|
||||
|
@ -13,6 +14,7 @@
|
|||
let dropdown
|
||||
|
||||
let editing
|
||||
let originalName = view.name
|
||||
|
||||
function showEditor() {
|
||||
editing = true
|
||||
|
@ -29,14 +31,18 @@
|
|||
DeleteViewModal,
|
||||
{
|
||||
onClosed: close,
|
||||
view,
|
||||
viewName: view.name,
|
||||
},
|
||||
{ styleContent: { padding: "0" } }
|
||||
)
|
||||
}
|
||||
|
||||
function save() {
|
||||
backendUiStore.actions.views.save(view)
|
||||
backendUiStore.actions.views.save({
|
||||
originalName,
|
||||
...view
|
||||
})
|
||||
notifier.success("Renamed View Successfully.")
|
||||
hideEditor()
|
||||
}
|
||||
</script>
|
||||
|
@ -48,7 +54,7 @@
|
|||
{#if editing}
|
||||
<h5>Edit View</h5>
|
||||
<div class="container">
|
||||
<Input placeholder="Table Name" thin bind:value={view} />
|
||||
<Input placeholder="Table Name" thin bind:value={view.name} />
|
||||
</div>
|
||||
<footer>
|
||||
<div class="button-margin-3">
|
||||
|
|
|
@ -12,7 +12,8 @@
|
|||
|
||||
const { open, close } = getContext("simple-modal")
|
||||
|
||||
$: selectedView = $backendUiStore.selectedView && $backendUiStore.selectedView.name
|
||||
$: selectedView =
|
||||
$backendUiStore.selectedView && $backendUiStore.selectedView.name
|
||||
|
||||
function selectModel(model) {
|
||||
backendUiStore.actions.models.select(model)
|
||||
|
@ -40,17 +41,18 @@
|
|||
on:click={() => selectModel(model)}>
|
||||
<EditTablePopover table={model} />
|
||||
</ListItem>
|
||||
{#each Object.keys(model.views || {}) as view}
|
||||
{#each Object.keys(model.views || {}) as viewName}
|
||||
<ListItem
|
||||
indented
|
||||
selected={selectedView === view}
|
||||
title={view}
|
||||
selected={selectedView === viewName}
|
||||
title={viewName}
|
||||
icon="ri-eye-line"
|
||||
on:click={() => selectView({
|
||||
name: view,
|
||||
...model.views[view]
|
||||
})}>
|
||||
<EditViewPopover {view} />
|
||||
name: viewName,
|
||||
...model.views[viewName],
|
||||
})}>
|
||||
<EditViewPopover
|
||||
view={{ name: viewName, ...model.views[viewName] }} />
|
||||
</ListItem>
|
||||
{/each}
|
||||
{/each}
|
||||
|
|
|
@ -13,7 +13,7 @@
|
|||
</script>
|
||||
|
||||
{#if $backendUiStore.selectedDatabase._id && selectedView}
|
||||
<ViewDataTable />
|
||||
<ViewDataTable view={selectedView} />
|
||||
{:else}
|
||||
<i>create your first table to start building</i>
|
||||
{/if}
|
||||
|
|
|
@ -80,10 +80,10 @@ exports.save = async function(ctx) {
|
|||
|
||||
exports.fetchView = async function(ctx) {
|
||||
const db = new CouchDB(ctx.user.instanceId)
|
||||
const { stats, groupBy } = ctx.query
|
||||
const { stats, group } = ctx.query
|
||||
const response = await db.query(`database/${ctx.params.viewName}`, {
|
||||
include_docs: !stats,
|
||||
group: !!groupBy
|
||||
group
|
||||
})
|
||||
|
||||
if (stats) {
|
||||
|
|
|
@ -2,22 +2,6 @@ const CouchDB = require("../../../db")
|
|||
const statsViewTemplate = require("./viewBuilder");
|
||||
|
||||
const controller = {
|
||||
query: async ctx => {
|
||||
const db = new CouchDB(ctx.user.instanceId)
|
||||
const { meta } = ctx.request.body
|
||||
const response = await db.query(`database/${ctx.params.viewName}`, {
|
||||
group: !!meta.groupBy
|
||||
})
|
||||
|
||||
for (row of response.rows) {
|
||||
row.value = {
|
||||
...row.value,
|
||||
avg: row.value.sum / row.value.count
|
||||
}
|
||||
}
|
||||
|
||||
ctx.body = response.rows
|
||||
},
|
||||
fetch: async ctx => {
|
||||
const db = new CouchDB(ctx.user.instanceId)
|
||||
const designDoc = await db.get("_design/database")
|
||||
|
@ -41,7 +25,7 @@ const controller = {
|
|||
},
|
||||
save: async ctx => {
|
||||
const db = new CouchDB(ctx.user.instanceId)
|
||||
const newView = ctx.request.body
|
||||
const { originalName, ...newView } = ctx.request.body
|
||||
|
||||
const designDoc = await db.get("_design/database")
|
||||
|
||||
|
@ -52,6 +36,11 @@ const controller = {
|
|||
[newView.name]: view,
|
||||
}
|
||||
|
||||
// view has been renamed
|
||||
if (originalName) {
|
||||
delete designDoc.views[originalName]
|
||||
}
|
||||
|
||||
await db.put(designDoc)
|
||||
|
||||
|
||||
|
@ -61,6 +50,11 @@ const controller = {
|
|||
...(model.views ? model.views : {}),
|
||||
[newView.name]: view.meta
|
||||
}
|
||||
|
||||
if (originalName) {
|
||||
delete model.views[originalName]
|
||||
}
|
||||
|
||||
await db.put(model)
|
||||
|
||||
ctx.body = view
|
||||
|
|
|
@ -13,7 +13,6 @@ router
|
|||
recordController.fetchView
|
||||
)
|
||||
.get("/api/views", authorized(BUILDER), viewController.fetch)
|
||||
// .post("/api/views/query/:viewName", authorized(BUILDER), viewController.query)
|
||||
.delete("/api/views/:viewName", authorized(BUILDER), viewController.destroy)
|
||||
.post("/api/views", authorized(BUILDER), viewController.save)
|
||||
|
||||
|
|
Loading…
Reference in New Issue