Add progress on calculation editor
This commit is contained in:
parent
d35fce699a
commit
608331018a
|
@ -1,29 +1,161 @@
|
||||||
<script>
|
<script>
|
||||||
import { ActionButton, Modal, ModalContent } from "@budibase/bbui"
|
import {
|
||||||
import { CalculationType } from "@budibase/types"
|
ActionButton,
|
||||||
import { API } from "api"
|
Modal,
|
||||||
import { getContext, createEventDispatcher } from "svelte"
|
ModalContent,
|
||||||
|
Select,
|
||||||
|
Icon,
|
||||||
|
} from "@budibase/bbui"
|
||||||
|
import { CalculationType, FieldType } from "@budibase/types"
|
||||||
|
import { getContext } from "svelte"
|
||||||
|
|
||||||
const { definition } = getContext("grid")
|
const { definition, datasource, rows } = getContext("grid")
|
||||||
const dispatch = createEventDispatcher()
|
const calculationTypeOptions = [
|
||||||
|
{
|
||||||
|
label: "Average (mean)",
|
||||||
|
value: CalculationType.AVG,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "Sum",
|
||||||
|
value: CalculationType.SUM,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "Minimum",
|
||||||
|
value: CalculationType.MIN,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "Maximum",
|
||||||
|
value: CalculationType.MAX,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "Count",
|
||||||
|
value: CalculationType.COUNT,
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
let modal
|
let modal
|
||||||
|
let calculations = []
|
||||||
|
let groupings = []
|
||||||
|
|
||||||
|
$: schema = $definition?.schema || {}
|
||||||
|
|
||||||
|
const open = () => {
|
||||||
|
calculations = extractCalculations(schema)
|
||||||
|
groupings = extractGroupings(schema)
|
||||||
|
modal?.show()
|
||||||
|
}
|
||||||
|
|
||||||
|
const extractCalculations = schema => {
|
||||||
|
if (!schema) {
|
||||||
|
return []
|
||||||
|
}
|
||||||
|
return Object.keys(schema)
|
||||||
|
.filter(field => {
|
||||||
|
return schema[field].calculationType != null
|
||||||
|
})
|
||||||
|
.map(field => ({
|
||||||
|
type: schema[field].calculationType,
|
||||||
|
field: schema[field].field,
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
|
const extractGroupings = schema => {
|
||||||
|
if (!schema) {
|
||||||
|
return []
|
||||||
|
}
|
||||||
|
return Object.keys(schema)
|
||||||
|
.filter(field => {
|
||||||
|
return schema[field].calculationType == null && schema[field].visible
|
||||||
|
})
|
||||||
|
.map(field => ({ field }))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Gets the available types for a given calculation
|
||||||
|
const getTypeOptions = (self, calculations) => {
|
||||||
|
return calculationTypeOptions.filter(option => {
|
||||||
|
return !calculations.some(
|
||||||
|
calc =>
|
||||||
|
calc !== self &&
|
||||||
|
calc.field === self.field &&
|
||||||
|
calc.type === option.value
|
||||||
|
)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Gets the available fields for a given calculation
|
||||||
|
const getFieldOptions = (self, calculations, schema) => {
|
||||||
|
return Object.entries(schema)
|
||||||
|
.filter(([field, fieldSchema]) => {
|
||||||
|
// Only allow numeric fields that are not calculations themselves
|
||||||
|
if (
|
||||||
|
fieldSchema.calculationType ||
|
||||||
|
fieldSchema.type !== FieldType.NUMBER
|
||||||
|
) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
// Don't allow duplicates
|
||||||
|
return !calculations.some(calc => {
|
||||||
|
return (
|
||||||
|
calc !== self && calc.type === self.type && calc.field === field
|
||||||
|
)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.map(([field]) => field)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Gets the available fields to group by for a given grouping
|
||||||
|
const getGroupingOptions = (self, groupings, schema) => {
|
||||||
|
return Object.entries(schema)
|
||||||
|
.filter(([field, fieldSchema]) => {
|
||||||
|
// Don't allow grouping by calculations
|
||||||
|
if (fieldSchema.calculationType) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
// Don't allow duplicates
|
||||||
|
return !groupings.some(grouping => {
|
||||||
|
return grouping !== self && grouping.field === field
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.map(([field]) => field)
|
||||||
|
}
|
||||||
|
|
||||||
|
const addCalc = () => {
|
||||||
|
calculations = [...calculations, {}]
|
||||||
|
}
|
||||||
|
|
||||||
|
const deleteCalc = idx => {
|
||||||
|
calculations = calculations.toSpliced(idx, 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
const addGrouping = () => {
|
||||||
|
groupings = [...groupings, {}]
|
||||||
|
}
|
||||||
|
|
||||||
|
const deleteGrouping = idx => {
|
||||||
|
groupings = groupings.toSpliced(idx, 1)
|
||||||
|
}
|
||||||
|
|
||||||
const save = async () => {
|
const save = async () => {
|
||||||
await API.viewV2.update({
|
let schema = {}
|
||||||
|
|
||||||
|
const rand = ("" + Math.random()).substring(2)
|
||||||
|
await datasource.actions.saveDefinition({
|
||||||
...$definition,
|
...$definition,
|
||||||
|
primaryDisplay: null,
|
||||||
schema: {
|
schema: {
|
||||||
"Average game length": {
|
["Average game length " + rand]: {
|
||||||
visible: true,
|
visible: true,
|
||||||
calculationType: CalculationType.AVG,
|
calculationType: CalculationType.AVG,
|
||||||
field: "Game Length",
|
field: "Game Length",
|
||||||
|
width: 300,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
await rows.actions.refreshData()
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<ActionButton icon="WebPage" quiet on:click={modal?.show}>
|
<ActionButton icon="WebPage" quiet on:click={open}>
|
||||||
Configure calculations
|
Configure calculations
|
||||||
</ActionButton>
|
</ActionButton>
|
||||||
|
|
||||||
|
@ -34,6 +166,76 @@
|
||||||
size="L"
|
size="L"
|
||||||
onConfirm={save}
|
onConfirm={save}
|
||||||
>
|
>
|
||||||
Show calculations which are based on
|
{#if !calculations.length}
|
||||||
|
<div>
|
||||||
|
<ActionButton quiet icon="Add">Add your first calculation</ActionButton>
|
||||||
|
</div>
|
||||||
|
{:else}
|
||||||
|
<div class="calculations">
|
||||||
|
{#each calculations as calc, idx}
|
||||||
|
<span>{idx === 0 ? "Calculate" : "and"} the</span>
|
||||||
|
<Select
|
||||||
|
options={getTypeOptions(calc, calculations)}
|
||||||
|
bind:value={calc.type}
|
||||||
|
placeholder="Function"
|
||||||
|
/>
|
||||||
|
<span>of</span>
|
||||||
|
<Select
|
||||||
|
options={getFieldOptions(calc, calculations, schema)}
|
||||||
|
bind:value={calc.field}
|
||||||
|
placeholder="Column"
|
||||||
|
/>
|
||||||
|
<Icon
|
||||||
|
hoverable
|
||||||
|
name="Close"
|
||||||
|
size="S"
|
||||||
|
on:click={() => deleteCalc(idx)}
|
||||||
|
color="var(--spectrum-global-color-gray-700)"
|
||||||
|
/>
|
||||||
|
{/each}
|
||||||
|
{#each groupings as group, idx}
|
||||||
|
<span>{idx === 0 ? "Group by" : "and"}</span>
|
||||||
|
<Select
|
||||||
|
options={getGroupingOptions(group, groupings, schema)}
|
||||||
|
bind:value={group.field}
|
||||||
|
placeholder="Column"
|
||||||
|
/>
|
||||||
|
<Icon
|
||||||
|
hoverable
|
||||||
|
name="Close"
|
||||||
|
size="S"
|
||||||
|
on:click={() => deleteGrouping(idx)}
|
||||||
|
color="var(--spectrum-global-color-gray-700)"
|
||||||
|
/>
|
||||||
|
<span />
|
||||||
|
<span />
|
||||||
|
{/each}
|
||||||
|
</div>
|
||||||
|
<div class="buttons">
|
||||||
|
<ActionButton quiet icon="Add" on:click={addCalc}>
|
||||||
|
Add calculation
|
||||||
|
</ActionButton>
|
||||||
|
<ActionButton quiet icon="Add" on:click={addGrouping}>
|
||||||
|
Group by
|
||||||
|
</ActionButton>
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
</ModalContent>
|
</ModalContent>
|
||||||
</Modal>
|
</Modal>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.calculations {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: auto 1fr auto 1fr auto;
|
||||||
|
align-items: center;
|
||||||
|
column-gap: var(--spacing-m);
|
||||||
|
row-gap: var(--spacing-m);
|
||||||
|
}
|
||||||
|
.buttons {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
}
|
||||||
|
span {
|
||||||
|
text-align: right;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
Loading…
Reference in New Issue