Add new builder store for row action CRUD, ensuring consistent state everywhere
This commit is contained in:
parent
0e6d903c74
commit
53ff7e1167
|
@ -9,21 +9,20 @@
|
||||||
} from "@budibase/bbui"
|
} from "@budibase/bbui"
|
||||||
import DetailPopover from "components/common/DetailPopover.svelte"
|
import DetailPopover from "components/common/DetailPopover.svelte"
|
||||||
import { getContext } from "svelte"
|
import { getContext } from "svelte"
|
||||||
import { appStore, automationStore } from "stores/builder"
|
import { appStore, rowActions } from "stores/builder"
|
||||||
import { API } from "api"
|
import { API } from "api"
|
||||||
import { goto, url } from "@roxi/routify"
|
import { goto, url } from "@roxi/routify"
|
||||||
import { derived } from "svelte/store"
|
import { derived } from "svelte/store"
|
||||||
import { getSequentialName } from "helpers/duplicate"
|
|
||||||
|
|
||||||
const { datasource } = getContext("grid")
|
const { datasource } = getContext("grid")
|
||||||
|
|
||||||
let rowActions = []
|
|
||||||
|
|
||||||
$: ds = $datasource
|
$: ds = $datasource
|
||||||
$: tableId = ds?.tableId
|
$: tableId = ds?.tableId
|
||||||
|
$: viewId = ds?.id
|
||||||
$: isView = ds?.type === "viewV2"
|
$: isView = ds?.type === "viewV2"
|
||||||
$: fetchRowActions(ds)
|
$: tableRowActions = $rowActions[tableId] || []
|
||||||
$: actionCount = rowActions.filter(action => !isView || action.enabled).length
|
$: viewRowActions = $rowActions[viewId] || []
|
||||||
|
$: actionCount = isView ? viewRowActions.length : tableRowActions.length
|
||||||
|
|
||||||
const rowActionUrl = derived([url, appStore], ([$url, $appStore]) => {
|
const rowActionUrl = derived([url, appStore], ([$url, $appStore]) => {
|
||||||
return ({ automationId }) => {
|
return ({ automationId }) => {
|
||||||
|
@ -31,30 +30,11 @@
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
const fetchRowActions = async datasource => {
|
|
||||||
if (!datasource?.tableId) {
|
|
||||||
rowActions = []
|
|
||||||
return
|
|
||||||
}
|
|
||||||
const res = await API.rowActions.fetch(datasource.tableId)
|
|
||||||
rowActions = Object.values(res || {}).map(action => ({
|
|
||||||
...action,
|
|
||||||
enabled: !isView || action.allowedViews?.includes(ds.id),
|
|
||||||
}))
|
|
||||||
}
|
|
||||||
|
|
||||||
const createRowAction = async () => {
|
const createRowAction = async () => {
|
||||||
try {
|
try {
|
||||||
const name = getSequentialName(rowActions, "New row action ", {
|
const newRowAction = await rowActions.createRowAction(tableId, viewId)
|
||||||
getName: x => x.name,
|
|
||||||
})
|
|
||||||
const res = await API.rowActions.create({
|
|
||||||
name,
|
|
||||||
tableId,
|
|
||||||
})
|
|
||||||
await automationStore.actions.fetch()
|
|
||||||
notifications.success("Row action created successfully")
|
notifications.success("Row action created successfully")
|
||||||
$goto($rowActionUrl(res))
|
// $goto($rowActionUrl(newRowAction))
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error(error)
|
console.error(error)
|
||||||
notifications.error("Error creating row action")
|
notifications.error("Error creating row action")
|
||||||
|
@ -62,19 +42,10 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
const toggleAction = async (action, enabled) => {
|
const toggleAction = async (action, enabled) => {
|
||||||
console.log(action, enabled)
|
|
||||||
if (enabled) {
|
if (enabled) {
|
||||||
await API.rowActions.enableView({
|
await rowActions.enableView(tableId, viewId, action.id)
|
||||||
tableId,
|
|
||||||
rowActionId: action.id,
|
|
||||||
viewId: ds.id,
|
|
||||||
})
|
|
||||||
} else {
|
} else {
|
||||||
await API.rowActions.disableView({
|
await rowActions.disableView(tableId, viewId, action.id)
|
||||||
tableId,
|
|
||||||
rowActionId: action.id,
|
|
||||||
viewId: ds.id,
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
@ -96,18 +67,18 @@
|
||||||
Use the toggle to enable/disable row actions for this view.
|
Use the toggle to enable/disable row actions for this view.
|
||||||
<br />
|
<br />
|
||||||
{/if}
|
{/if}
|
||||||
{#if !rowActions.length}
|
{#if !tableRowActions.length}
|
||||||
<br />
|
<br />
|
||||||
You haven't created any row actions.
|
You haven't created any row actions.
|
||||||
{:else}
|
{:else}
|
||||||
<List>
|
<List>
|
||||||
{#each rowActions as action}
|
{#each tableRowActions as action}
|
||||||
<ListItem title={action.name} url={$rowActionUrl(action)} showArrow>
|
<ListItem title={action.name} url={$rowActionUrl(action)} showArrow>
|
||||||
<svelte:fragment slot="right">
|
<svelte:fragment slot="right">
|
||||||
{#if isView}
|
{#if isView}
|
||||||
<span>
|
<span>
|
||||||
<Toggle
|
<Toggle
|
||||||
value={action.enabled}
|
value={action.allowedViews?.includes(viewId)}
|
||||||
on:change={e => toggleAction(action, e.detail)}
|
on:change={e => toggleAction(action, e.detail)}
|
||||||
/>
|
/>
|
||||||
</span>
|
</span>
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
<script>
|
<script>
|
||||||
import { viewsV2 } from "stores/builder"
|
import { viewsV2, rowActions } from "stores/builder"
|
||||||
import { admin } from "stores/portal"
|
import { admin } from "stores/portal"
|
||||||
import { Grid } from "@budibase/frontend-core"
|
import { Grid } from "@budibase/frontend-core"
|
||||||
import { API } from "api"
|
import { API } from "api"
|
||||||
|
@ -21,6 +21,21 @@
|
||||||
id,
|
id,
|
||||||
tableId: $viewsV2.selected?.tableId,
|
tableId: $viewsV2.selected?.tableId,
|
||||||
}
|
}
|
||||||
|
$: buttons = makeRowActionButtons($rowActions[id])
|
||||||
|
$: rowActions.refreshRowActions(id)
|
||||||
|
|
||||||
|
const makeRowActionButtons = rowActions => {
|
||||||
|
return (rowActions || []).map(action => ({
|
||||||
|
text: action.name,
|
||||||
|
onClick: async row => {
|
||||||
|
await API.rowActions.trigger({
|
||||||
|
rowActionId: action.id,
|
||||||
|
sourceId: id,
|
||||||
|
rowId: row._id,
|
||||||
|
})
|
||||||
|
},
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
const handleGridViewUpdate = async e => {
|
const handleGridViewUpdate = async e => {
|
||||||
viewsV2.replaceView(id, e.detail)
|
viewsV2.replaceView(id, e.detail)
|
||||||
|
@ -35,6 +50,8 @@
|
||||||
showAvatars={false}
|
showAvatars={false}
|
||||||
on:updatedatasource={handleGridViewUpdate}
|
on:updatedatasource={handleGridViewUpdate}
|
||||||
isCloud={$admin.cloud}
|
isCloud={$admin.cloud}
|
||||||
|
{buttons}
|
||||||
|
buttonsCollapsed
|
||||||
>
|
>
|
||||||
<svelte:fragment slot="controls">
|
<svelte:fragment slot="controls">
|
||||||
<GridFilterButton />
|
<GridFilterButton />
|
||||||
|
|
|
@ -1,6 +1,12 @@
|
||||||
<script>
|
<script>
|
||||||
import { Banner } from "@budibase/bbui"
|
import { Banner } from "@budibase/bbui"
|
||||||
import { datasources, tables, integrations, appStore } from "stores/builder"
|
import {
|
||||||
|
datasources,
|
||||||
|
tables,
|
||||||
|
integrations,
|
||||||
|
appStore,
|
||||||
|
rowActions,
|
||||||
|
} from "stores/builder"
|
||||||
import { themeStore, admin } from "stores/portal"
|
import { themeStore, admin } from "stores/portal"
|
||||||
import { TableNames } from "constants"
|
import { TableNames } from "constants"
|
||||||
import { Grid } from "@budibase/frontend-core"
|
import { Grid } from "@budibase/frontend-core"
|
||||||
|
@ -28,7 +34,6 @@
|
||||||
status: { displayName: "Status", disabled: true },
|
status: { displayName: "Status", disabled: true },
|
||||||
}
|
}
|
||||||
|
|
||||||
let rowActions = []
|
|
||||||
let generateButton
|
let generateButton
|
||||||
|
|
||||||
$: autoColumnStatus = verifyAutocolumns($tables?.selected)
|
$: autoColumnStatus = verifyAutocolumns($tables?.selected)
|
||||||
|
@ -54,11 +59,11 @@
|
||||||
$: relationshipsEnabled = relationshipSupport(tableDatasource)
|
$: relationshipsEnabled = relationshipSupport(tableDatasource)
|
||||||
$: currentTheme = $themeStore?.theme
|
$: currentTheme = $themeStore?.theme
|
||||||
$: darkMode = !currentTheme.includes("light")
|
$: darkMode = !currentTheme.includes("light")
|
||||||
$: buttons = makeRowActionButtons(rowActions)
|
$: buttons = makeRowActionButtons($rowActions[id])
|
||||||
$: fetchRowActions(id)
|
$: rowActions.refreshRowActions(id)
|
||||||
|
|
||||||
const makeRowActionButtons = rowActions => {
|
const makeRowActionButtons = rowActions => {
|
||||||
return rowActions.map(action => ({
|
return (rowActions || []).map(action => ({
|
||||||
text: action.name,
|
text: action.name,
|
||||||
onClick: async row => {
|
onClick: async row => {
|
||||||
await API.rowActions.trigger({
|
await API.rowActions.trigger({
|
||||||
|
@ -98,15 +103,6 @@
|
||||||
return acc
|
return acc
|
||||||
}, {})
|
}, {})
|
||||||
}
|
}
|
||||||
|
|
||||||
const fetchRowActions = async tableId => {
|
|
||||||
if (!tableId) {
|
|
||||||
rowActions = []
|
|
||||||
return
|
|
||||||
}
|
|
||||||
const res = await API.rowActions.fetch(tableId)
|
|
||||||
rowActions = Object.values(res || {})
|
|
||||||
}
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
{#if $tables?.selected?.name}
|
{#if $tables?.selected?.name}
|
||||||
|
|
|
@ -2,9 +2,8 @@ import { writable } from "svelte/store"
|
||||||
|
|
||||||
export default class BudiStore {
|
export default class BudiStore {
|
||||||
constructor(init, opts) {
|
constructor(init, opts) {
|
||||||
const store = writable({
|
this.initialState = init
|
||||||
...init,
|
const store = writable({ ...init })
|
||||||
})
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Internal Svelte store
|
* Internal Svelte store
|
||||||
|
@ -23,6 +22,7 @@ export default class BudiStore {
|
||||||
* *Store modification should be kept to a minimum
|
* *Store modification should be kept to a minimum
|
||||||
*/
|
*/
|
||||||
this.update = this.store.update
|
this.update = this.store.update
|
||||||
|
this.set = this.store.set
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Optional debug mode to output the store updates to console
|
* Optional debug mode to output the store updates to console
|
||||||
|
@ -33,4 +33,8 @@ export default class BudiStore {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
reset = () => {
|
||||||
|
this.store.set({ ...this.initialState })
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -29,6 +29,7 @@ import { integrations } from "./integrations"
|
||||||
import { sortedIntegrations } from "./sortedIntegrations"
|
import { sortedIntegrations } from "./sortedIntegrations"
|
||||||
import { queries } from "./queries"
|
import { queries } from "./queries"
|
||||||
import { flags } from "./flags"
|
import { flags } from "./flags"
|
||||||
|
import { rowActions } from "./rowActions"
|
||||||
import componentTreeNodesStore from "./componentTreeNodes"
|
import componentTreeNodesStore from "./componentTreeNodes"
|
||||||
|
|
||||||
export {
|
export {
|
||||||
|
@ -65,6 +66,7 @@ export {
|
||||||
flags,
|
flags,
|
||||||
hoverStore,
|
hoverStore,
|
||||||
snippets,
|
snippets,
|
||||||
|
rowActions,
|
||||||
}
|
}
|
||||||
|
|
||||||
export const reset = () => {
|
export const reset = () => {
|
||||||
|
@ -74,6 +76,7 @@ export const reset = () => {
|
||||||
componentStore.reset()
|
componentStore.reset()
|
||||||
layoutStore.reset()
|
layoutStore.reset()
|
||||||
navigationStore.reset()
|
navigationStore.reset()
|
||||||
|
rowActions.reset()
|
||||||
}
|
}
|
||||||
|
|
||||||
const refreshBuilderData = async () => {
|
const refreshBuilderData = async () => {
|
||||||
|
|
|
@ -0,0 +1,114 @@
|
||||||
|
import { get, derived } from "svelte/store"
|
||||||
|
import BudiStore from "stores/BudiStore"
|
||||||
|
import { tables } from "./tables"
|
||||||
|
import { viewsV2 } from "./viewsV2"
|
||||||
|
import { automationStore } from "./automations"
|
||||||
|
import { API } from "api"
|
||||||
|
import { getSequentialName } from "helpers/duplicate"
|
||||||
|
|
||||||
|
const initialState = {}
|
||||||
|
|
||||||
|
export class RowActionStore extends BudiStore {
|
||||||
|
constructor() {
|
||||||
|
super(initialState)
|
||||||
|
}
|
||||||
|
|
||||||
|
refreshRowActions = async sourceId => {
|
||||||
|
if (!sourceId) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the underlying table ID for this source ID
|
||||||
|
let tableId = get(tables).list.find(table => table._id === sourceId)?._id
|
||||||
|
if (!tableId) {
|
||||||
|
const view = get(viewsV2).list.find(view => view.id === sourceId)
|
||||||
|
tableId = view?.tableId
|
||||||
|
}
|
||||||
|
if (!tableId) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fetch row actions for this table
|
||||||
|
const res = await API.rowActions.fetch(tableId)
|
||||||
|
const actions = Object.values(res || {})
|
||||||
|
this.update(state => ({
|
||||||
|
...state,
|
||||||
|
[tableId]: actions,
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
|
createRowAction = async (tableId, viewId) => {
|
||||||
|
if (!tableId) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get a unique name for this action
|
||||||
|
const existingRowActions = get(this.store)[tableId] || []
|
||||||
|
const name = getSequentialName(existingRowActions, "New row action ", {
|
||||||
|
getName: x => x.name,
|
||||||
|
})
|
||||||
|
|
||||||
|
// Create the action and update state
|
||||||
|
const res = await API.rowActions.create({
|
||||||
|
name,
|
||||||
|
tableId,
|
||||||
|
})
|
||||||
|
this.update(state => ({
|
||||||
|
...state,
|
||||||
|
[tableId]: [...(state[tableId] || []), res],
|
||||||
|
}))
|
||||||
|
|
||||||
|
// If adding to a view, enable on this view
|
||||||
|
if (viewId) {
|
||||||
|
await this.enableView(tableId, viewId, res.id)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Refresh automations so we have this new row action automation
|
||||||
|
await automationStore.actions.fetch()
|
||||||
|
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
|
||||||
|
enableView = async (tableId, viewId, rowActionId) => {
|
||||||
|
await API.rowActions.enableView({
|
||||||
|
tableId,
|
||||||
|
viewId,
|
||||||
|
rowActionId,
|
||||||
|
})
|
||||||
|
await this.refreshRowActions(tableId)
|
||||||
|
}
|
||||||
|
|
||||||
|
disableView = async (tableId, viewId, rowActionId) => {
|
||||||
|
await API.rowActions.disableView({
|
||||||
|
tableId,
|
||||||
|
viewId,
|
||||||
|
rowActionId,
|
||||||
|
})
|
||||||
|
await this.refreshRowActions(tableId)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const store = new RowActionStore()
|
||||||
|
const derivedStore = derived(store, $store => {
|
||||||
|
let map = {}
|
||||||
|
|
||||||
|
// Generate an entry for every view as well
|
||||||
|
Object.keys($store || {}).forEach(tableId => {
|
||||||
|
map[tableId] = $store[tableId]
|
||||||
|
for (let action of $store[tableId]) {
|
||||||
|
for (let viewId of action.allowedViews || []) {
|
||||||
|
if (!map[viewId]) {
|
||||||
|
map[viewId] = []
|
||||||
|
}
|
||||||
|
map[viewId].push(action)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
return map
|
||||||
|
})
|
||||||
|
|
||||||
|
export const rowActions = {
|
||||||
|
...store,
|
||||||
|
subscribe: derivedStore.subscribe,
|
||||||
|
}
|
|
@ -104,7 +104,6 @@ export const createLicensingStore = () => {
|
||||||
const isBusinessPlan = planType === Constants.PlanType.BUSINESS
|
const isBusinessPlan = planType === Constants.PlanType.BUSINESS
|
||||||
const isEnterpriseTrial =
|
const isEnterpriseTrial =
|
||||||
planType === Constants.PlanType.ENTERPRISE_BASIC_TRIAL
|
planType === Constants.PlanType.ENTERPRISE_BASIC_TRIAL
|
||||||
console.log(license)
|
|
||||||
const groupsEnabled = license.features.includes(
|
const groupsEnabled = license.features.includes(
|
||||||
Constants.Features.USER_GROUPS
|
Constants.Features.USER_GROUPS
|
||||||
)
|
)
|
||||||
|
@ -143,8 +142,6 @@ export const createLicensingStore = () => {
|
||||||
Constants.Features.VIEW_READONLY_COLUMNS
|
Constants.Features.VIEW_READONLY_COLUMNS
|
||||||
)
|
)
|
||||||
|
|
||||||
console.log(isViewReadonlyColumnsEnabled)
|
|
||||||
|
|
||||||
store.update(state => {
|
store.update(state => {
|
||||||
return {
|
return {
|
||||||
...state,
|
...state,
|
||||||
|
|
|
@ -25,7 +25,7 @@
|
||||||
|
|
||||||
let container
|
let container
|
||||||
|
|
||||||
$: buttons = $props.buttons?.slice(0, 3) || []
|
$: buttons = getButtons($props)
|
||||||
$: columnsWidth = $scrollableColumns.reduce(
|
$: columnsWidth = $scrollableColumns.reduce(
|
||||||
(total, col) => (total += col.width),
|
(total, col) => (total += col.width),
|
||||||
0
|
0
|
||||||
|
@ -34,6 +34,14 @@
|
||||||
$: gridEnd = $width - $buttonColumnWidth - 1
|
$: gridEnd = $width - $buttonColumnWidth - 1
|
||||||
$: left = Math.min(columnEnd, gridEnd)
|
$: left = Math.min(columnEnd, gridEnd)
|
||||||
|
|
||||||
|
const getButtons = ({ buttons, buttonsCollapsed }) => {
|
||||||
|
let gridButtons = buttons || []
|
||||||
|
if (!buttonsCollapsed) {
|
||||||
|
return gridButtons.slice(0, 3)
|
||||||
|
}
|
||||||
|
return gridButtons
|
||||||
|
}
|
||||||
|
|
||||||
const handleClick = async (button, row) => {
|
const handleClick = async (button, row) => {
|
||||||
await button.onClick?.(rows.actions.cleanRow(row))
|
await button.onClick?.(rows.actions.cleanRow(row))
|
||||||
await rows.actions.refreshRow(row._id)
|
await rows.actions.refreshRow(row._id)
|
||||||
|
|
Loading…
Reference in New Issue