From 95faeea28638f234342c42ea87c39512c8509bf5 Mon Sep 17 00:00:00 2001 From: Adria Navarro Date: Tue, 18 Jul 2023 18:32:31 +0200 Subject: [PATCH 001/145] datasourceType setup --- .../builder/src/components/backend/DataTable/DataTable.svelte | 1 + packages/client/src/components/app/GridBlock.svelte | 1 + packages/frontend-core/src/components/grid/layout/Grid.svelte | 2 ++ packages/frontend-core/src/components/grid/stores/rows.js | 3 ++- 4 files changed, 6 insertions(+), 1 deletion(-) diff --git a/packages/builder/src/components/backend/DataTable/DataTable.svelte b/packages/builder/src/components/backend/DataTable/DataTable.svelte index 33db9b60e3..3fcaefc5bd 100644 --- a/packages/builder/src/components/backend/DataTable/DataTable.svelte +++ b/packages/builder/src/components/backend/DataTable/DataTable.svelte @@ -44,6 +44,7 @@ { loading, sort, tableId, + datasourceType, API, scroll, validation, @@ -111,7 +112,7 @@ export const deriveStores = context => { const newFetch = fetchData({ API, datasource: { - type: "table", + type: datasourceType, tableId: $tableId, }, options: { From 9ccc54773dbdea33264677caf24d7a4f5c383715 Mon Sep 17 00:00:00 2001 From: Adria Navarro Date: Tue, 18 Jul 2023 18:32:52 +0200 Subject: [PATCH 002/145] ViewV2 page --- .../TableNavigator/TableNavigator.svelte | 10 +++++++-- .../data/view/v2/[viewName].svelte | 22 +++++++++++++++++++ 2 files changed, 30 insertions(+), 2 deletions(-) create mode 100644 packages/builder/src/pages/builder/app/[application]/data/view/v2/[viewName].svelte diff --git a/packages/builder/src/components/backend/TableNavigator/TableNavigator.svelte b/packages/builder/src/components/backend/TableNavigator/TableNavigator.svelte index d9def682dc..ef8872c1ca 100644 --- a/packages/builder/src/components/backend/TableNavigator/TableNavigator.svelte +++ b/packages/builder/src/components/backend/TableNavigator/TableNavigator.svelte @@ -37,13 +37,19 @@ {/if} - {#each [...Object.keys(table.views || {})].sort() as viewName, idx (idx)} + {#each [...Object.entries(table.views || {})].sort() as [viewName, view], idx (idx)} $goto(`./view/${encodeURIComponent(viewName)}`)} + on:click={() => { + if (view.version === 2) { + $goto(`./view/v2/${encodeURIComponent(viewName)}`) + } else { + $goto(`./view/${encodeURIComponent(viewName)}`) + } + }} selectedBy={$userSelectedResourceMap[viewName]} > + import { views } from "stores/backend" + import { Grid } from "@budibase/frontend-core" + import { API } from "api" + + $: tableId = $views.selected?.id + + +
+ +
+ + From 82ea9a7cc1c35ed1a4db616b3b5924c425b74eed Mon Sep 17 00:00:00 2001 From: Adria Navarro Date: Wed, 19 Jul 2023 10:14:42 +0200 Subject: [PATCH 003/145] Setup datasource type --- .../data/view/v2/{[viewName].svelte => [id].svelte} | 10 ++++++++-- .../frontend-core/src/components/grid/stores/rows.js | 4 ++-- 2 files changed, 10 insertions(+), 4 deletions(-) rename packages/builder/src/pages/builder/app/[application]/data/view/v2/{[viewName].svelte => [id].svelte} (73%) diff --git a/packages/builder/src/pages/builder/app/[application]/data/view/v2/[viewName].svelte b/packages/builder/src/pages/builder/app/[application]/data/view/v2/[id].svelte similarity index 73% rename from packages/builder/src/pages/builder/app/[application]/data/view/v2/[viewName].svelte rename to packages/builder/src/pages/builder/app/[application]/data/view/v2/[id].svelte index 3df89bd777..266fd237f5 100644 --- a/packages/builder/src/pages/builder/app/[application]/data/view/v2/[viewName].svelte +++ b/packages/builder/src/pages/builder/app/[application]/data/view/v2/[id].svelte @@ -3,11 +3,17 @@ import { Grid } from "@budibase/frontend-core" import { API } from "api" - $: tableId = $views.selected?.id + export let id
- +
diff --git a/packages/builder/src/components/backend/DatasourceNavigator/DatasourceNavigator.svelte b/packages/builder/src/components/backend/DatasourceNavigator/DatasourceNavigator.svelte index f7b6f61a10..8f5a35ea1f 100644 --- a/packages/builder/src/components/backend/DatasourceNavigator/DatasourceNavigator.svelte +++ b/packages/builder/src/components/backend/DatasourceNavigator/DatasourceNavigator.svelte @@ -1,7 +1,14 @@ diff --git a/packages/builder/src/components/backend/TableNavigator/TableNavigator.svelte b/packages/builder/src/components/backend/TableNavigator/TableNavigator.svelte index e641bd99a5..caa9de222a 100644 --- a/packages/builder/src/components/backend/TableNavigator/TableNavigator.svelte +++ b/packages/builder/src/components/backend/TableNavigator/TableNavigator.svelte @@ -1,5 +1,5 @@ -
- -
- - + diff --git a/packages/builder/src/stores/backend/index.js b/packages/builder/src/stores/backend/index.js index 6fbc9f82c7..278e43c1ed 100644 --- a/packages/builder/src/stores/backend/index.js +++ b/packages/builder/src/stores/backend/index.js @@ -1,6 +1,7 @@ export { database } from "./database" export { tables } from "./tables" export { views } from "./views" +export { viewsV2 } from "./viewsV2" export { permissions } from "./permissions" export { roles } from "./roles" export { datasources, ImportTableError } from "./datasources" diff --git a/packages/builder/src/stores/backend/views.js b/packages/builder/src/stores/backend/views.js index 8df337a299..603b0830fc 100644 --- a/packages/builder/src/stores/backend/views.js +++ b/packages/builder/src/stores/backend/views.js @@ -9,7 +9,10 @@ export function createViewsStore() { const derivedStore = derived([store, tables], ([$store, $tables]) => { let list = [] $tables.list?.forEach(table => { - list = list.concat(Object.values(table?.views || {})) + const views = Object.values(table?.views || {}).filter(view => { + return view.version !== 2 + }) + list = list.concat(views) }) return { ...$store, @@ -26,11 +29,7 @@ export function createViewsStore() { } const deleteView = async view => { - if (view.version === 2) { - await API.viewV2.delete(view.id) - } else { - await API.deleteView(view.name) - } + await API.deleteView(view.name) // Update tables tables.update(state => { @@ -40,20 +39,6 @@ export function createViewsStore() { }) } - const create = async view => { - const savedViewResponse = await API.viewV2.create(view) - const savedView = savedViewResponse.data - - // Update tables - tables.update(state => { - const table = state.list.find(table => table._id === view.tableId) - table.views[view.name] = savedView - return { ...state } - }) - - return savedView - } - const save = async view => { const savedView = await API.saveView(view) @@ -74,7 +59,6 @@ export function createViewsStore() { subscribe: derivedStore.subscribe, select, delete: deleteView, - create, save, } } diff --git a/packages/builder/src/stores/backend/viewsV2.js b/packages/builder/src/stores/backend/viewsV2.js new file mode 100644 index 0000000000..8b7b1d876c --- /dev/null +++ b/packages/builder/src/stores/backend/viewsV2.js @@ -0,0 +1,85 @@ +import { writable, derived } from "svelte/store" +import { tables } from "./" +import { API } from "api" + +export function createViewsV2Store() { + const store = writable({ + selectedViewId: null, + }) + const derivedStore = derived([store, tables], ([$store, $tables]) => { + let list = [] + $tables.list?.forEach(table => { + const views = Object.values(table?.views || {}).filter(view => { + return view.version === 2 + }) + list = list.concat(views) + }) + return { + ...$store, + list, + selected: list.find(view => view.id === $store.selectedViewId), + } + }) + + const select = id => { + store.update(state => ({ + ...state, + selectedViewId: id, + })) + } + + const deleteView = async view => { + await API.viewV2.delete(view.id) + + // Update tables + tables.update(state => { + const table = state.list.find(table => table._id === view.tableId) + delete table.views[view.name] + return { ...state } + }) + } + + const create = async view => { + const savedViewResponse = await API.viewV2.create(view) + const savedView = savedViewResponse.data + + // Update tables + tables.update(state => { + const table = state.list.find(table => table._id === view.tableId) + table.views[view.name] = savedView + return { ...state } + }) + + return savedView + } + + const save = async view => { + // No dedicated save endpoint at this time + // const savedView = await API.saveView(view) + // + // // Update tables + // tables.update(state => { + // const table = state.list.find(table => table._id === view.tableId) + // if (table) { + // if (view.originalName) { + // delete table.views[view.originalName] + // } + // table.views[view.name] = savedView + // } + // return { ...state } + // }) + } + + const replace = (id, view) => {} + + return { + subscribe: derivedStore.subscribe, + select, + delete: deleteView, + create, + save, + replace, + } +} + +export const viewsV2 = createViewsV2Store() diff --git a/packages/frontend-core/src/components/grid/layout/Grid.svelte b/packages/frontend-core/src/components/grid/layout/Grid.svelte index d92851719f..556e6ca16e 100644 --- a/packages/frontend-core/src/components/grid/layout/Grid.svelte +++ b/packages/frontend-core/src/components/grid/layout/Grid.svelte @@ -28,8 +28,7 @@ } from "../lib/constants" export let API = null - export let tableId = null - export let datasourceType = null + export let datasource = null export let schemaOverrides = null export let columnWhitelist = null export let allowAddRows = true @@ -77,8 +76,7 @@ // Keep config store up to date with props $: config.set({ - tableId, - datasourceType, + datasource, schemaOverrides, columnWhitelist, allowAddRows, diff --git a/packages/frontend-core/src/components/grid/layout/HeaderRow.svelte b/packages/frontend-core/src/components/grid/layout/HeaderRow.svelte index f39e679da4..d964931682 100644 --- a/packages/frontend-core/src/components/grid/layout/HeaderRow.svelte +++ b/packages/frontend-core/src/components/grid/layout/HeaderRow.svelte @@ -12,7 +12,7 @@ width, config, hasNonAutoColumn, - tableId, + datasource, loading, } = getContext("grid") @@ -33,7 +33,7 @@ {#if $config.allowSchemaChanges} - {#key $tableId} + {#key $datasource} { - const { rows, tableId, users, focusedCellId, table, API } = context + const { rows, datasource, users, focusedCellId, table, API } = context const socket = createWebsocket("/socket/grid") - const connectToTable = tableId => { + const connectToDatasource = datasource => { if (!socket.connected) { return } // Identify which table we are editing const appId = API.getAppID() socket.emit( - GridSocketEvent.SelectTable, - { tableId, appId }, + GridSocketEvent.SelectDatasource, + { datasource, appId }, ({ users: gridUsers }) => { users.set(gridUsers) } @@ -23,7 +23,7 @@ export const createGridWebsocket = context => { // Built-in events socket.on("connect", () => { - connectToTable(get(tableId)) + connectToDatasource(get(datasource)) }) socket.on("connect_error", err => { console.log("Failed to connect to grid websocket:", err.message) @@ -57,7 +57,7 @@ export const createGridWebsocket = context => { }) // Change websocket connection when table changes - tableId.subscribe(connectToTable) + datasource.subscribe(connectToDatasource) // Notify selected cell changes focusedCellId.subscribe($focusedCellId => { diff --git a/packages/frontend-core/src/components/grid/stores/config.js b/packages/frontend-core/src/components/grid/stores/config.js index fc9decc1ef..8aa6b818e2 100644 --- a/packages/frontend-core/src/components/grid/stores/config.js +++ b/packages/frontend-core/src/components/grid/stores/config.js @@ -6,7 +6,7 @@ export const createStores = context => { const getProp = prop => derivedMemo(config, $config => $config[prop]) // Derive and memoize some props so that we can react to them in isolation - const tableId = getProp("tableId") + const datasource = getProp("datasource") const initialSortColumn = getProp("initialSortColumn") const initialSortOrder = getProp("initialSortOrder") const initialFilter = getProp("initialFilter") @@ -18,7 +18,7 @@ export const createStores = context => { return { config, - tableId, + datasource, initialSortColumn, initialSortOrder, initialFilter, diff --git a/packages/frontend-core/src/components/grid/stores/rows.js b/packages/frontend-core/src/components/grid/stores/rows.js index dccbc774a4..1346d7c8b2 100644 --- a/packages/frontend-core/src/components/grid/stores/rows.js +++ b/packages/frontend-core/src/components/grid/stores/rows.js @@ -59,7 +59,7 @@ export const deriveStores = context => { filter, loading, sort, - tableId, + datasource, API, scroll, validation, @@ -71,7 +71,7 @@ export const deriveStores = context => { hasNextPage, error, notifications, - props, + config, } = context const instanceLoaded = writable(false) const fetch = writable(null) @@ -95,7 +95,7 @@ export const deriveStores = context => { // Reset everything when table ID changes let unsubscribe = null let lastResetKey = null - tableId.subscribe(async $tableId => { + datasource.subscribe(async $datasource => { // Unsub from previous fetch if one exists unsubscribe?.() fetch.set(null) @@ -108,21 +108,6 @@ export const deriveStores = context => { const $filter = get(filter) const $sort = get(sort) - let datasource - if (props.datasourceType === "viewV2") { - const tableId = $tableId - datasource = { - type: props.datasourceType, - id: $tableId, - tableId: tableId.split("_").slice(0, -1).join("_"), - } - } else { - datasource = { - type: props.datasourceType, - tableId: $tableId, - } - } - // Create new fetch model const newFetch = fetchData({ API, From 352b7ebe1ccb2ac84b95294309ed4ea28d8a9354 Mon Sep 17 00:00:00 2001 From: Andrew Kingston Date: Wed, 26 Jul 2023 14:07:07 +0100 Subject: [PATCH 022/145] Add dedicated route for routes v1, improve view creation modal, fix selection state --- .../DataTable/modals/CreateViewModal.svelte | 19 ++++++----- .../TableNavigator/TableNavigator.svelte | 32 ++++++++++--------- .../view/{ => v1}/[viewName]/_layout.svelte | 2 +- .../view/{ => v1}/[viewName]/index.svelte | 0 .../[application]/data/view/v1/index.svelte | 5 +++ .../view/v2/{[id] => [viewId]}/_layout.svelte | 2 +- .../view/v2/{[id] => [viewId]}/index.svelte | 0 .../[application]/data/view/v2/index.svelte | 5 +++ .../src/components/grid/stores/rows.js | 2 +- 9 files changed, 41 insertions(+), 26 deletions(-) rename packages/builder/src/pages/builder/app/[application]/data/view/{ => v1}/[viewName]/_layout.svelte (95%) rename packages/builder/src/pages/builder/app/[application]/data/view/{ => v1}/[viewName]/index.svelte (100%) create mode 100644 packages/builder/src/pages/builder/app/[application]/data/view/v1/index.svelte rename packages/builder/src/pages/builder/app/[application]/data/view/v2/{[id] => [viewId]}/_layout.svelte (96%) rename packages/builder/src/pages/builder/app/[application]/data/view/v2/{[id] => [viewId]}/index.svelte (100%) create mode 100644 packages/builder/src/pages/builder/app/[application]/data/view/v2/index.svelte diff --git a/packages/builder/src/components/backend/DataTable/modals/CreateViewModal.svelte b/packages/builder/src/components/backend/DataTable/modals/CreateViewModal.svelte index 6a7f5b96a1..ac5e522923 100644 --- a/packages/builder/src/components/backend/DataTable/modals/CreateViewModal.svelte +++ b/packages/builder/src/components/backend/DataTable/modals/CreateViewModal.svelte @@ -1,22 +1,19 @@ {#if $database?._id} @@ -37,28 +45,22 @@ {/if}
- {#each [...Object.entries(table.views || {})].sort() as [viewName, view], idx (idx)} - {@const viewSelected = - $isActive("./view") && $views.selected?.name === viewName} - {@const viewV2Selected = - $isActive("./view/v2") && $viewsV2.selected?.name === viewName} + {#each [...Object.entries(table.views || {})].sort() as [name, view], idx (idx)} { if (view.version === 2) { $goto(`./view/v2/${view.id}`) } else { - $goto(`./view/${encodeURIComponent(viewName)}`) + $goto(`./view/v1/${encodeURIComponent(name)}`) } }} - selectedBy={$userSelectedResourceMap[viewName]} + selectedBy={$userSelectedResourceMap[name]} > - + {/each} {/each} diff --git a/packages/builder/src/pages/builder/app/[application]/data/view/[viewName]/_layout.svelte b/packages/builder/src/pages/builder/app/[application]/data/view/v1/[viewName]/_layout.svelte similarity index 95% rename from packages/builder/src/pages/builder/app/[application]/data/view/[viewName]/_layout.svelte rename to packages/builder/src/pages/builder/app/[application]/data/view/v1/[viewName]/_layout.svelte index f3793317e8..7f4fc9c597 100644 --- a/packages/builder/src/pages/builder/app/[application]/data/view/[viewName]/_layout.svelte +++ b/packages/builder/src/pages/builder/app/[application]/data/view/v1/[viewName]/_layout.svelte @@ -13,7 +13,7 @@ stateKey: "selectedViewName", validate: name => $views.list?.some(view => view.name === name), update: views.select, - fallbackUrl: "../", + fallbackUrl: "../../", store: views, routify, decode: decodeURIComponent, diff --git a/packages/builder/src/pages/builder/app/[application]/data/view/[viewName]/index.svelte b/packages/builder/src/pages/builder/app/[application]/data/view/v1/[viewName]/index.svelte similarity index 100% rename from packages/builder/src/pages/builder/app/[application]/data/view/[viewName]/index.svelte rename to packages/builder/src/pages/builder/app/[application]/data/view/v1/[viewName]/index.svelte diff --git a/packages/builder/src/pages/builder/app/[application]/data/view/v1/index.svelte b/packages/builder/src/pages/builder/app/[application]/data/view/v1/index.svelte new file mode 100644 index 0000000000..c11ca87023 --- /dev/null +++ b/packages/builder/src/pages/builder/app/[application]/data/view/v1/index.svelte @@ -0,0 +1,5 @@ + diff --git a/packages/builder/src/pages/builder/app/[application]/data/view/v2/[id]/_layout.svelte b/packages/builder/src/pages/builder/app/[application]/data/view/v2/[viewId]/_layout.svelte similarity index 96% rename from packages/builder/src/pages/builder/app/[application]/data/view/v2/[id]/_layout.svelte rename to packages/builder/src/pages/builder/app/[application]/data/view/v2/[viewId]/_layout.svelte index 62844a54df..8ddd6adbd0 100644 --- a/packages/builder/src/pages/builder/app/[application]/data/view/v2/[id]/_layout.svelte +++ b/packages/builder/src/pages/builder/app/[application]/data/view/v2/[viewId]/_layout.svelte @@ -9,7 +9,7 @@ $: store.actions.websocket.selectResource(id) const stopSyncing = syncURLToState({ - urlParam: "id", + urlParam: "viewId", stateKey: "selectedViewId", validate: id => $viewsV2.list?.some(view => view.id === id), update: viewsV2.select, diff --git a/packages/builder/src/pages/builder/app/[application]/data/view/v2/[id]/index.svelte b/packages/builder/src/pages/builder/app/[application]/data/view/v2/[viewId]/index.svelte similarity index 100% rename from packages/builder/src/pages/builder/app/[application]/data/view/v2/[id]/index.svelte rename to packages/builder/src/pages/builder/app/[application]/data/view/v2/[viewId]/index.svelte diff --git a/packages/builder/src/pages/builder/app/[application]/data/view/v2/index.svelte b/packages/builder/src/pages/builder/app/[application]/data/view/v2/index.svelte new file mode 100644 index 0000000000..c11ca87023 --- /dev/null +++ b/packages/builder/src/pages/builder/app/[application]/data/view/v2/index.svelte @@ -0,0 +1,5 @@ + diff --git a/packages/frontend-core/src/components/grid/stores/rows.js b/packages/frontend-core/src/components/grid/stores/rows.js index 1346d7c8b2..5d3cd20109 100644 --- a/packages/frontend-core/src/components/grid/stores/rows.js +++ b/packages/frontend-core/src/components/grid/stores/rows.js @@ -111,7 +111,7 @@ export const deriveStores = context => { // Create new fetch model const newFetch = fetchData({ API, - datasource, + datasource: $datasource, options: { filter: $filter, sortColumn: $sort.column, From 2d3da0dfcf6213e4b2412c625074535d27ffc8c3 Mon Sep 17 00:00:00 2001 From: Andrew Kingston Date: Wed, 26 Jul 2023 14:26:34 +0100 Subject: [PATCH 023/145] Fix null issues in view fetch, fix edit view popover, improve handling of nullish view params --- .../backend/DataTable/ViewV2DataTable.svelte | 3 +- .../TableNavigator/TableNavigator.svelte | 2 +- .../popovers/EditViewPopover.svelte | 33 ++++++++++--------- packages/builder/src/helpers/urlStateSync.js | 2 +- .../app/[application]/data/view/index.svelte | 15 +++++---- .../src/components/grid/stores/rows.js | 5 +++ .../frontend-core/src/fetch/ViewV2Fetch.js | 6 ++-- 7 files changed, 39 insertions(+), 27 deletions(-) diff --git a/packages/builder/src/components/backend/DataTable/ViewV2DataTable.svelte b/packages/builder/src/components/backend/DataTable/ViewV2DataTable.svelte index 6d549147fb..028030bb9a 100644 --- a/packages/builder/src/components/backend/DataTable/ViewV2DataTable.svelte +++ b/packages/builder/src/components/backend/DataTable/ViewV2DataTable.svelte @@ -1,5 +1,5 @@ - + diff --git a/packages/frontend-core/src/components/grid/stores/rows.js b/packages/frontend-core/src/components/grid/stores/rows.js index 80e4e6039d..23eae75428 100644 --- a/packages/frontend-core/src/components/grid/stores/rows.js +++ b/packages/frontend-core/src/components/grid/stores/rows.js @@ -482,9 +482,14 @@ export const createActions = context => { } // Refreshes the schema of the data fetch subscription - const refreshTableDefinition = async () => { - const definition = await API.fetchTableDefinition(get(tableId)) - table.set(definition) + const refreshDatasourceDefinition = async () => { + const $datasource = get(datasource) + if ($datasource.type === "table") { + table.set(await API.fetchTableDefinition($datasource.tableId)) + } else if ($datasource.type === "viewV2") { + // const definition = await API.viewsV2.(get(tableId)) + // table.set(definition) + } } // Checks if we have a row with a certain ID @@ -520,7 +525,7 @@ export const createActions = context => { refreshRow, replaceRow, refreshData, - refreshTableDefinition, + refreshDatasourceDefinition, }, }, } From 9665ec34dd7de7dadc0cc49cf8fced0f88568950 Mon Sep 17 00:00:00 2001 From: Andrew Kingston Date: Thu, 27 Jul 2023 15:53:50 +0100 Subject: [PATCH 026/145] Adjust grid props and config --- .../src/components/grid/layout/Grid.svelte | 24 +++++++++---------- .../src/components/grid/stores/config.js | 16 +++---------- 2 files changed, 15 insertions(+), 25 deletions(-) diff --git a/packages/frontend-core/src/components/grid/layout/Grid.svelte b/packages/frontend-core/src/components/grid/layout/Grid.svelte index ea7cd73a4b..343419af58 100644 --- a/packages/frontend-core/src/components/grid/layout/Grid.svelte +++ b/packages/frontend-core/src/components/grid/layout/Grid.svelte @@ -32,12 +32,12 @@ export let datasource = null export let schemaOverrides = null export let columnWhitelist = null - export let allowAddRows = true - export let allowExpandRows = true - export let allowEditRows = true - export let allowDeleteRows = true - export let allowEditColumns = true - export let allowSchemaChanges = true + export let canAddRows = true + export let canExpandRows = true + export let canEditRows = true + export let canDeleteRows = true + export let canEditColumns = true + export let canSaveSchema = true export let stripeRows = false export let collaboration = true export let showAvatars = true @@ -83,12 +83,12 @@ datasource, schemaOverrides, columnWhitelist, - allowAddRows, - allowExpandRows, - allowEditRows, - allowDeleteRows, - allowEditColumns, - allowSchemaChanges, + canAddRows, + canExpandRows, + canEditRows, + canDeleteRows, + canEditColumns, + canSaveSchema, stripeRows, collaboration, showAvatars, diff --git a/packages/frontend-core/src/components/grid/stores/config.js b/packages/frontend-core/src/components/grid/stores/config.js index 82ec8e29f3..0ed04bf741 100644 --- a/packages/frontend-core/src/components/grid/stores/config.js +++ b/packages/frontend-core/src/components/grid/stores/config.js @@ -21,16 +21,10 @@ export const deriveStores = context => { [props, hasNonAutoColumn], ([$props, $hasNonAutoColumn]) => { let config = { - // Row features - canAddRows: $props.allowAddRows, - canExpandRows: $props.allowExpandRows, - canEditRows: $props.allowEditRows, - canDeleteRows: $props.allowDeleteRows, + ...$props, - // Column features - canEditColumns: $props.allowEditColumns, - canEditPrimaryDisplay: $props.allowEditColumns, - canSaveSchema: $props.allowSchemaChanges, + // Additional granular features which we don't expose as props + canEditPrimaryDisplay: $props.canEditColumns, } // Disable some features if we're editing a view @@ -42,15 +36,11 @@ export const deriveStores = context => { config.canAddRows = false } - console.log($hasNonAutoColumn) - // Disable adding rows if we don't have any valid columns if (!$hasNonAutoColumn) { config.canAddRows = false } - console.log(config) - return config } ) From d83820b583bbaf0c4c661a00f9848513c4b94f3c Mon Sep 17 00:00:00 2001 From: Andrew Kingston Date: Fri, 28 Jul 2023 15:57:35 +0100 Subject: [PATCH 027/145] Fix adding rows --- packages/frontend-core/src/api/rows.js | 2 +- .../src/components/grid/stores/index.js | 2 +- .../src/components/grid/stores/rows.js | 17 ++++++++++++----- 3 files changed, 14 insertions(+), 7 deletions(-) diff --git a/packages/frontend-core/src/api/rows.js b/packages/frontend-core/src/api/rows.js index 8e8570ea2a..3878b1f08b 100644 --- a/packages/frontend-core/src/api/rows.js +++ b/packages/frontend-core/src/api/rows.js @@ -23,7 +23,7 @@ export const buildRowEndpoints = API => ({ return } return await API.post({ - url: `/api/${row.tableId}/rows`, + url: `/api/${row.viewId || row.tableId}/rows`, body: row, suppressErrors, }) diff --git a/packages/frontend-core/src/components/grid/stores/index.js b/packages/frontend-core/src/components/grid/stores/index.js index 985fad8c4a..d287025342 100644 --- a/packages/frontend-core/src/components/grid/stores/index.js +++ b/packages/frontend-core/src/components/grid/stores/index.js @@ -22,8 +22,8 @@ const DependencyOrderedStores = [ Filter, Bounds, Scroll, - Rows, Columns, + Rows, UI, Validation, Resize, diff --git a/packages/frontend-core/src/components/grid/stores/rows.js b/packages/frontend-core/src/components/grid/stores/rows.js index 23eae75428..69dd7ae97a 100644 --- a/packages/frontend-core/src/components/grid/stores/rows.js +++ b/packages/frontend-core/src/components/grid/stores/rows.js @@ -90,7 +90,6 @@ export const createActions = context => { } = context const instanceLoaded = writable(false) const fetch = writable(null) - const tableId = writable(null) // Local cache of row IDs to speed up checking if a row exists let rowCacheMap = {} @@ -261,10 +260,18 @@ export const createActions = context => { const addRow = async (row, idx, bubble = false) => { try { // Create row - const newRow = await API.saveRow( - { ...row, tableId: get(tableId) }, - SuppressErrors - ) + const $datasource = get(datasource) + let newRow = { ...row } + if ($datasource.type === "table") { + newRow.tableId = $datasource.tableId + } else if ($datasource.type === "viewV2") { + newRow.tableId = $datasource.tableId + newRow._viewId = $datasource.id + } else { + return + } + console.log(newRow) + newRow = await API.saveRow(newRow, SuppressErrors) // Update state if (idx != null) { From c7c9bd656385d27a41127a52039f35d46638ddc9 Mon Sep 17 00:00:00 2001 From: Andrew Kingston Date: Fri, 28 Jul 2023 16:01:18 +0100 Subject: [PATCH 028/145] Fix row deletion and fetching for tables --- packages/frontend-core/src/components/grid/stores/rows.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/frontend-core/src/components/grid/stores/rows.js b/packages/frontend-core/src/components/grid/stores/rows.js index 69dd7ae97a..af7c698014 100644 --- a/packages/frontend-core/src/components/grid/stores/rows.js +++ b/packages/frontend-core/src/components/grid/stores/rows.js @@ -312,7 +312,7 @@ export const createActions = context => { // Fetches a row by ID using the search endpoint const fetchRow = async id => { const res = await API.searchTable({ - tableId: get(tableId), + tableId: get(datasource).tableId, limit: 1, query: { equal: { @@ -442,7 +442,7 @@ export const createActions = context => { delete row.__idx }) await API.deleteRows({ - tableId: get(tableId), + tableId: get(datasource).tableId, rows: rowsToDelete, }) From 1aea6fce095b1db31485c3f6569eb70a8c23113a Mon Sep 17 00:00:00 2001 From: Andrew Kingston Date: Fri, 28 Jul 2023 16:02:31 +0100 Subject: [PATCH 029/145] Fix grid import and export for tables --- .../backend/DataTable/buttons/grid/GridExportButton.svelte | 4 ++-- .../backend/DataTable/buttons/grid/GridImportButton.svelte | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/builder/src/components/backend/DataTable/buttons/grid/GridExportButton.svelte b/packages/builder/src/components/backend/DataTable/buttons/grid/GridExportButton.svelte index b5fe202d11..f1bbc04328 100644 --- a/packages/builder/src/components/backend/DataTable/buttons/grid/GridExportButton.svelte +++ b/packages/builder/src/components/backend/DataTable/buttons/grid/GridExportButton.svelte @@ -2,7 +2,7 @@ import ExportButton from "../ExportButton.svelte" import { getContext } from "svelte" - const { rows, columns, tableId, sort, selectedRows, filter } = + const { rows, columns, datasource, sort, selectedRows, filter } = getContext("grid") $: disabled = !$rows.length || !$columns.length @@ -12,7 +12,7 @@ From 8204935dfa9948c17580c25b11a63a71805eaafe Mon Sep 17 00:00:00 2001 From: Andrew Kingston Date: Sun, 30 Jul 2023 12:49:07 +0100 Subject: [PATCH 030/145] Add ability to update views, create views with existing filters and sorting applied --- .../DataTable/modals/CreateViewModal.svelte | 15 +++++++++++++-- .../src/components/grid/lib/websocket.js | 5 ++++- .../src/components/grid/stores/columns.js | 14 +++++++++++--- packages/frontend-core/src/fetch/ViewV2Fetch.js | 4 +--- packages/server/src/sdk/app/views/index.ts | 1 + 5 files changed, 30 insertions(+), 9 deletions(-) diff --git a/packages/builder/src/components/backend/DataTable/modals/CreateViewModal.svelte b/packages/builder/src/components/backend/DataTable/modals/CreateViewModal.svelte index ac5e522923..b5c3fb6e31 100644 --- a/packages/builder/src/components/backend/DataTable/modals/CreateViewModal.svelte +++ b/packages/builder/src/components/backend/DataTable/modals/CreateViewModal.svelte @@ -1,11 +1,17 @@ @@ -35,7 +37,6 @@ align="right" offset={0} popoverTarget={document.getElementById(`add-column-button`)} - animate={false} customZindex={100} >
Date: Tue, 1 Aug 2023 10:54:01 +0100 Subject: [PATCH 033/145] Disable collab for views --- packages/frontend-core/src/components/grid/lib/websocket.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/frontend-core/src/components/grid/lib/websocket.js b/packages/frontend-core/src/components/grid/lib/websocket.js index 1c490f74ca..2f9ebf59ad 100644 --- a/packages/frontend-core/src/components/grid/lib/websocket.js +++ b/packages/frontend-core/src/components/grid/lib/websocket.js @@ -7,7 +7,7 @@ export const createGridWebsocket = context => { const socket = createWebsocket("/socket/grid") const connectToDatasource = datasource => { - if (!socket.connected) { + if (!socket.connected || datasource?.type !== "table") { return } // Identify which table we are editing From 9d2b31af54232c03cca73c060c63642b53ba975d Mon Sep 17 00:00:00 2001 From: Andrew Kingston Date: Tue, 1 Aug 2023 11:16:10 +0100 Subject: [PATCH 034/145] Enable creating and updating rows through views --- packages/frontend-core/src/api/viewsV2.js | 43 +++++++++++++++++++ .../grid/overlays/KeyboardManager.svelte | 6 +-- .../src/components/grid/stores/config.js | 2 - .../src/components/grid/stores/rows.js | 43 +++++++++++-------- 4 files changed, 71 insertions(+), 23 deletions(-) diff --git a/packages/frontend-core/src/api/viewsV2.js b/packages/frontend-core/src/api/viewsV2.js index 37bc78ae16..81fcfcf455 100644 --- a/packages/frontend-core/src/api/viewsV2.js +++ b/packages/frontend-core/src/api/viewsV2.js @@ -33,4 +33,47 @@ export const buildViewV2Endpoints = API => ({ delete: async viewId => { return await API.delete({ url: `/api/v2/views/${viewId}` }) }, + /** + * Creates a row from a view + * @param row the row to create + * @param suppressErrors whether or not to suppress error notifications + */ + createRow: async (row, suppressErrors = false) => { + if (!row?._viewId || !row?.tableId) { + return + } + return await API.post({ + url: `/api/v2/views/${row._viewId}/rows`, + body: row, + suppressErrors, + }) + }, + /** + * Updates an existing row through a view + * @param row the row to update + * @param suppressErrors whether or not to suppress error notifications + */ + updateRow: async (row, suppressErrors = false) => { + if (!row?._viewId || !row?.tableId || !row?._id) { + return + } + return await API.patch({ + url: `/api/v2/views/${row._viewId}/rows/${row._id}`, + body: row, + suppressErrors, + }) + }, + /** + * Deletes multiple rows from a table through a view + * @param viewId the table ID to delete the rows from + * @param rows the array of rows to delete + */ + deleteRows: async ({ viewId, rows }) => { + return await API.delete({ + url: `/api/v2/views/${viewId}/rows`, + body: { + rows, + }, + }) + }, }) diff --git a/packages/frontend-core/src/components/grid/overlays/KeyboardManager.svelte b/packages/frontend-core/src/components/grid/overlays/KeyboardManager.svelte index 721babd913..4f30c3c7db 100644 --- a/packages/frontend-core/src/components/grid/overlays/KeyboardManager.svelte +++ b/packages/frontend-core/src/components/grid/overlays/KeyboardManager.svelte @@ -4,7 +4,7 @@ import { NewRowID } from "../lib/constants" const { - enrichedRows, + rows, focusedCellId, visibleColumns, focusedRow, @@ -142,7 +142,7 @@ // Focuses the first cell in the grid const focusFirstCell = () => { - const firstRow = $enrichedRows[0] + const firstRow = $rows[0] if (!firstRow) { return } @@ -183,7 +183,7 @@ if (!$focusedRow) { return } - const newRow = $enrichedRows[$focusedRow.__idx + delta] + const newRow = $rows[$focusedRow.__idx + delta] if (newRow) { const split = $focusedCellId.split("-") $focusedCellId = `${newRow._id}-${split[1]}` diff --git a/packages/frontend-core/src/components/grid/stores/config.js b/packages/frontend-core/src/components/grid/stores/config.js index 0ed04bf741..9a72e781f7 100644 --- a/packages/frontend-core/src/components/grid/stores/config.js +++ b/packages/frontend-core/src/components/grid/stores/config.js @@ -31,9 +31,7 @@ export const deriveStores = context => { if ($props.datasource?.type === "viewV2") { config.canEditPrimaryDisplay = false config.canEditColumns = false - config.canEditRows = false config.canDeleteRows = false - config.canAddRows = false } // Disable adding rows if we don't have any valid columns diff --git a/packages/frontend-core/src/components/grid/stores/rows.js b/packages/frontend-core/src/components/grid/stores/rows.js index 35bacb786a..ac50672e07 100644 --- a/packages/frontend-core/src/components/grid/stores/rows.js +++ b/packages/frontend-core/src/components/grid/stores/rows.js @@ -16,17 +16,13 @@ export const createStores = () => { const error = writable(null) // Generate a lookup map to quick find a row by ID - const rowLookupMap = derived( - rows, - $rows => { - let map = {} - for (let i = 0; i < $rows.length; i++) { - map[$rows[i]._id] = i - } - return map - }, - {} - ) + const rowLookupMap = derived(rows, $rows => { + let map = {} + for (let i = 0; i < $rows.length; i++) { + map[$rows[i]._id] = i + } + return map + }) // Mark loaded as true if we've ever stopped loading let hasStartedLoading = false @@ -47,8 +43,7 @@ export const createStores = () => { ...$rowChangeCache[row._id], __idx: idx, })) - }, - [] + } ) return { @@ -262,13 +257,14 @@ export const createActions = context => { let newRow = { ...row } if ($datasource.type === "table") { newRow.tableId = $datasource.tableId + newRow = await API.saveRow(newRow, SuppressErrors) } else if ($datasource.type === "viewV2") { newRow.tableId = $datasource.tableId newRow._viewId = $datasource.id + newRow = await API.viewV2.createRow(newRow) } else { return } - newRow = await API.saveRow(newRow, SuppressErrors) // Update state if (idx != null) { @@ -362,6 +358,7 @@ export const createActions = context => { const updateRow = async (rowId, changes) => { const $rows = get(rows) const $rowLookupMap = get(rowLookupMap) + const $datasource = get(datasource) const index = $rowLookupMap[rowId] const row = $rows[index] if (index == null || !Object.keys(changes || {}).length) { @@ -395,10 +392,20 @@ export const createActions = context => { ...state, [rowId]: true, })) - const saved = await API.saveRow( - { ...row, ...get(rowChangeCache)[rowId] }, - SuppressErrors - ) + + let saved + if ($datasource.type === "table") { + saved = await API.saveRow( + { ...row, ...get(rowChangeCache)[rowId] }, + SuppressErrors + ) + } else if ($datasource.type === "viewV2") { + saved = await API.viewV2.updateRow( + { ...row, ...get(rowChangeCache)[rowId] }, + SuppressErrors + ) + saved._viewId = $datasource.id + } // Update state after a successful change if (saved?._id) { From 1d21b4260abd6ffe4ce1fc30bf5a3d1364b24f92 Mon Sep 17 00:00:00 2001 From: Andrew Kingston Date: Tue, 1 Aug 2023 11:21:22 +0100 Subject: [PATCH 035/145] Enable deleting rows through views --- packages/frontend-core/src/api/viewsV2.js | 4 ++++ .../src/components/grid/stores/config.js | 1 - .../src/components/grid/stores/rows.js | 16 ++++++++++++---- 3 files changed, 16 insertions(+), 5 deletions(-) diff --git a/packages/frontend-core/src/api/viewsV2.js b/packages/frontend-core/src/api/viewsV2.js index 81fcfcf455..f2848b55f3 100644 --- a/packages/frontend-core/src/api/viewsV2.js +++ b/packages/frontend-core/src/api/viewsV2.js @@ -69,6 +69,10 @@ export const buildViewV2Endpoints = API => ({ * @param rows the array of rows to delete */ deleteRows: async ({ viewId, rows }) => { + // Ensure we delete _viewId from rows as otherwise this throws a 500 + rows?.forEach(row => { + delete row?._viewId + }) return await API.delete({ url: `/api/v2/views/${viewId}/rows`, body: { diff --git a/packages/frontend-core/src/components/grid/stores/config.js b/packages/frontend-core/src/components/grid/stores/config.js index 9a72e781f7..a0b1be19da 100644 --- a/packages/frontend-core/src/components/grid/stores/config.js +++ b/packages/frontend-core/src/components/grid/stores/config.js @@ -31,7 +31,6 @@ export const deriveStores = context => { if ($props.datasource?.type === "viewV2") { config.canEditPrimaryDisplay = false config.canEditColumns = false - config.canDeleteRows = false } // Disable adding rows if we don't have any valid columns diff --git a/packages/frontend-core/src/components/grid/stores/rows.js b/packages/frontend-core/src/components/grid/stores/rows.js index ac50672e07..ed9f99a3f6 100644 --- a/packages/frontend-core/src/components/grid/stores/rows.js +++ b/packages/frontend-core/src/components/grid/stores/rows.js @@ -440,15 +440,23 @@ export const createActions = context => { if (!rowsToDelete?.length) { return } + const $datasource = get(datasource) // Actually delete rows rowsToDelete.forEach(row => { delete row.__idx }) - await API.deleteRows({ - tableId: get(datasource).tableId, - rows: rowsToDelete, - }) + if ($datasource.type === "table") { + await API.deleteRows({ + tableId: $datasource.tableId, + rows: rowsToDelete, + }) + } else if ($datasource.type === "viewV2") { + await API.viewV2.deleteRows({ + viewId: $datasource.id, + rows: rowsToDelete, + }) + } // Update state handleRemoveRows(rowsToDelete) From cb01768e5a9b3154c0cea6f5f1aa4c3237098014 Mon Sep 17 00:00:00 2001 From: Andrew Kingston Date: Tue, 1 Aug 2023 14:18:12 +0100 Subject: [PATCH 036/145] Fix crash when schemaNonUI does not exist --- packages/server/src/api/controllers/view/viewsV2.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/server/src/api/controllers/view/viewsV2.ts b/packages/server/src/api/controllers/view/viewsV2.ts index cd28a1b55f..e2ac4c83fa 100644 --- a/packages/server/src/api/controllers/view/viewsV2.ts +++ b/packages/server/src/api/controllers/view/viewsV2.ts @@ -18,7 +18,7 @@ async function parseSchemaUI(ctx: Ctx, view: CreateViewRequest) { newObj: Record, existingObj: Record ) { - const result = Object.entries(newObj).some(([key, value]) => { + const result = Object.entries(newObj || {}).some(([key, value]) => { const isObject = typeof value === "object" const existing = existingObj[key] if (isObject && hasOverrides(value, existing || {})) { From bb810e14d73b5a2c1357bf8948935af119070c7e Mon Sep 17 00:00:00 2001 From: Andrew Kingston Date: Tue, 1 Aug 2023 14:24:06 +0100 Subject: [PATCH 037/145] Fix issue with order 0 being ignored --- packages/server/src/sdk/app/views/index.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/server/src/sdk/app/views/index.ts b/packages/server/src/sdk/app/views/index.ts index 7fa5165220..e9f678371c 100644 --- a/packages/server/src/sdk/app/views/index.ts +++ b/packages/server/src/sdk/app/views/index.ts @@ -77,13 +77,13 @@ export function enrichSchema(view: View | ViewV2, tableSchema: TableSchema) { let schema = { ...tableSchema } if (view.schemaUI) { const viewOverridesEntries = Object.entries(view.schemaUI) - const viewSetsOrder = viewOverridesEntries.some(([_, v]) => v.order) + const viewSetsOrder = viewOverridesEntries.some(([_, v]) => v.order != null) for (const [fieldName, schemaUI] of viewOverridesEntries) { schema[fieldName] = { ...schema[fieldName], ...schemaUI, order: viewSetsOrder - ? schemaUI.order || undefined + ? schemaUI.order ?? undefined : schema[fieldName].order, } } From d825c32fdfbe5933a71c412066a2596e6c92f409 Mon Sep 17 00:00:00 2001 From: Andrew Kingston Date: Tue, 1 Aug 2023 14:25:16 +0100 Subject: [PATCH 038/145] Hide access button for now --- .../src/components/backend/DataTable/ViewV2DataTable.svelte | 2 -- 1 file changed, 2 deletions(-) diff --git a/packages/builder/src/components/backend/DataTable/ViewV2DataTable.svelte b/packages/builder/src/components/backend/DataTable/ViewV2DataTable.svelte index 028030bb9a..21b3e1c291 100644 --- a/packages/builder/src/components/backend/DataTable/ViewV2DataTable.svelte +++ b/packages/builder/src/components/backend/DataTable/ViewV2DataTable.svelte @@ -4,7 +4,6 @@ import { API } from "api" import GridCreateEditRowModal from "components/backend/DataTable/modals/grid/GridCreateEditRowModal.svelte" import GridFilterButton from "components/backend/DataTable/buttons/grid/GridFilterButton.svelte" - import GridManageAccessButton from "components/backend/DataTable/buttons/grid/GridManageAccessButton.svelte" $: id = $viewsV2.selected?.id $: datasource = { @@ -31,7 +30,6 @@ - From 3482ec3d9e238f0bf13851f51dc7bda2b54ffbe9 Mon Sep 17 00:00:00 2001 From: Andrew Kingston Date: Tue, 1 Aug 2023 15:34:02 +0100 Subject: [PATCH 039/145] Add feature flags to view V2 fetch --- .../src/components/grid/stores/rows.js | 33 +++++++++++++++---- .../frontend-core/src/fetch/ViewV2Fetch.js | 8 +++++ 2 files changed, 34 insertions(+), 7 deletions(-) diff --git a/packages/frontend-core/src/components/grid/stores/rows.js b/packages/frontend-core/src/components/grid/stores/rows.js index ed9f99a3f6..3c33d92d25 100644 --- a/packages/frontend-core/src/components/grid/stores/rows.js +++ b/packages/frontend-core/src/components/grid/stores/rows.js @@ -169,6 +169,8 @@ export const createActions = context => { scroll.update(state => ({ ...state, top: 0 })) } + // For views we always update the filter to match the definition + // Process new rows handleNewRows($fetch.rows, resetRows) @@ -182,15 +184,19 @@ export const createActions = context => { // Update fetch when filter or sort config changes filter.subscribe($filter => { - get(fetch)?.update({ - filter: $filter, - }) + if (get(datasource)?.type === "table") { + get(fetch)?.update({ + filter: $filter, + }) + } }) sort.subscribe($sort => { - get(fetch)?.update({ - sortOrder: $sort.order, - sortColumn: $sort.column, - }) + if (get(datasource)?.type === "table") { + get(fetch)?.update({ + sortOrder: $sort.order, + sortColumn: $sort.column, + }) + } }) // Gets a row by ID @@ -549,3 +555,16 @@ export const createActions = context => { }, } } + +export const initialise = context => { + const { table, filter, datasource } = context + + // For views, always keep the UI for filter and sorting up to date with the + // latest view definition + table.subscribe($definition => { + if (!$definition || get(datasource)?.type !== "viewV2") { + return + } + filter.set($definition.query) + }) +} diff --git a/packages/frontend-core/src/fetch/ViewV2Fetch.js b/packages/frontend-core/src/fetch/ViewV2Fetch.js index 3713aab877..9bc61f0383 100644 --- a/packages/frontend-core/src/fetch/ViewV2Fetch.js +++ b/packages/frontend-core/src/fetch/ViewV2Fetch.js @@ -1,6 +1,14 @@ import DataFetch from "./DataFetch.js" export default class ViewV2Fetch extends DataFetch { + determineFeatureFlags() { + return { + supportsSearch: true, + supportsSort: true, + supportsPagination: true, + } + } + async getSchema(datasource, definition) { return definition?.schema } From ab47e49dd946c5e85ea4e7fc050ad8b6b40dffdf Mon Sep 17 00:00:00 2001 From: Andrew Kingston Date: Wed, 2 Aug 2023 15:27:34 +0100 Subject: [PATCH 040/145] Update create view modal to only depend on grid context --- .../backend/DataTable/modals/CreateViewModal.svelte | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/packages/builder/src/components/backend/DataTable/modals/CreateViewModal.svelte b/packages/builder/src/components/backend/DataTable/modals/CreateViewModal.svelte index b5c3fb6e31..afb9efa53f 100644 --- a/packages/builder/src/components/backend/DataTable/modals/CreateViewModal.svelte +++ b/packages/builder/src/components/backend/DataTable/modals/CreateViewModal.svelte @@ -3,17 +3,16 @@ import { Input, notifications, ModalContent } from "@budibase/bbui" import { goto } from "@roxi/routify" import { viewsV2 } from "stores/backend" - import { tables } from "stores/backend" import { LuceneUtils } from "@budibase/frontend-core" const { filter, sort, table } = getContext("grid") $: query = LuceneUtils.buildLuceneQuery($filter) - $: console.log($table.schema) let name - $: views = Object.keys($tables.selected?.views || {}) + $: console.log($table) + $: views = Object.keys($table?.views || {}) $: nameExists = views.includes(name?.trim()) const saveView = async () => { @@ -21,17 +20,19 @@ try { const newView = await viewsV2.create({ name, - tableId: $tables.selected._id, + tableId: $table._id, query, sort: { field: $sort.column, order: $sort.order, }, - columns: $table.schema, + schema: $table.schema, + primaryDisplay: $table.primaryDisplay, }) notifications.success(`View ${name} created`) $goto(`../../view/v2/${newView.id}`) } catch (error) { + console.log(error) notifications.error("Error creating view") } } From e3cf0667be65faf53ba72bcce1b5f288d47b27a5 Mon Sep 17 00:00:00 2001 From: Andrew Kingston Date: Thu, 3 Aug 2023 11:18:19 +0100 Subject: [PATCH 041/145] Refactor grid to split up stores and provide better separation of datasource-specific logic --- .../backend/DataTable/TableDataTable.svelte | 2 +- .../buttons/grid/GridFilterButton.svelte | 10 +- .../buttons/grid/GridImportButton.svelte | 6 +- .../grid/GridManageAccessButton.svelte | 4 +- .../grid/GridRelationshipButton.svelte | 6 +- .../modals/grid/GridCreateColumnModal.svelte | 4 +- .../grid/controls/SizeButton.svelte | 14 ++- .../src/components/grid/index.js | 1 + .../src/components/grid/lib/constants.js | 4 + .../src/components/grid/stores/columns.js | 96 +++------------- .../src/components/grid/stores/config.js | 4 +- .../src/components/grid/stores/datsource.js | 107 ++++++++++++++++++ .../src/components/grid/stores/index.js | 9 ++ .../src/components/grid/stores/rows.js | 73 +++--------- .../src/components/grid/stores/sort.js | 3 +- .../src/components/grid/stores/table.js | 24 ++++ .../src/components/grid/stores/ui.js | 8 +- .../src/components/grid/stores/viewV2.js | 43 +++++++ packages/frontend-core/src/fetch/DataFetch.js | 10 +- 19 files changed, 262 insertions(+), 166 deletions(-) create mode 100644 packages/frontend-core/src/components/grid/stores/datsource.js create mode 100644 packages/frontend-core/src/components/grid/stores/table.js create mode 100644 packages/frontend-core/src/components/grid/stores/viewV2.js diff --git a/packages/builder/src/components/backend/DataTable/TableDataTable.svelte b/packages/builder/src/components/backend/DataTable/TableDataTable.svelte index 33175b336d..109c965271 100644 --- a/packages/builder/src/components/backend/DataTable/TableDataTable.svelte +++ b/packages/builder/src/components/backend/DataTable/TableDataTable.svelte @@ -61,7 +61,7 @@ allowDeleteRows={!isUsersTable} schemaOverrides={isUsersTable ? userSchemaOverrides : null} showAvatars={false} - on:updatetable={handleGridTableUpdate} + on:updatedatasource={handleGridTableUpdate} > diff --git a/packages/builder/src/components/backend/DataTable/buttons/grid/GridFilterButton.svelte b/packages/builder/src/components/backend/DataTable/buttons/grid/GridFilterButton.svelte index 45e5d4e2b0..ae4d39483e 100644 --- a/packages/builder/src/components/backend/DataTable/buttons/grid/GridFilterButton.svelte +++ b/packages/builder/src/components/backend/DataTable/buttons/grid/GridFilterButton.svelte @@ -2,22 +2,22 @@ import TableFilterButton from "../TableFilterButton.svelte" import { getContext } from "svelte" - const { columns, tableId, filter, table } = getContext("grid") + const { columns, datasource, filter, definition } = getContext("grid") // Wipe filter whenever table ID changes to avoid using stale filters - $: $tableId, filter.set([]) + $: $datasource, filter.set([]) const onFilter = e => { filter.set(e.detail || []) } -{#key $tableId} +{#key $datasource} {/key} diff --git a/packages/builder/src/components/backend/DataTable/buttons/grid/GridImportButton.svelte b/packages/builder/src/components/backend/DataTable/buttons/grid/GridImportButton.svelte index 667cf5e89a..71d971891c 100644 --- a/packages/builder/src/components/backend/DataTable/buttons/grid/GridImportButton.svelte +++ b/packages/builder/src/components/backend/DataTable/buttons/grid/GridImportButton.svelte @@ -4,12 +4,12 @@ export let disabled = false - const { rows, datasource, table } = getContext("grid") + const { rows, datasource, definition } = getContext("grid") diff --git a/packages/builder/src/components/backend/DataTable/buttons/grid/GridManageAccessButton.svelte b/packages/builder/src/components/backend/DataTable/buttons/grid/GridManageAccessButton.svelte index 154007950a..cc15ae564e 100644 --- a/packages/builder/src/components/backend/DataTable/buttons/grid/GridManageAccessButton.svelte +++ b/packages/builder/src/components/backend/DataTable/buttons/grid/GridManageAccessButton.svelte @@ -2,7 +2,7 @@ import ManageAccessButton from "../ManageAccessButton.svelte" import { getContext } from "svelte" - const { tableId } = getContext("grid") + const { datasource } = getContext("grid") - + diff --git a/packages/builder/src/components/backend/DataTable/buttons/grid/GridRelationshipButton.svelte b/packages/builder/src/components/backend/DataTable/buttons/grid/GridRelationshipButton.svelte index 460391366f..baa7dbed14 100644 --- a/packages/builder/src/components/backend/DataTable/buttons/grid/GridRelationshipButton.svelte +++ b/packages/builder/src/components/backend/DataTable/buttons/grid/GridRelationshipButton.svelte @@ -2,12 +2,12 @@ import ExistingRelationshipButton from "../ExistingRelationshipButton.svelte" import { getContext } from "svelte" - const { table, rows } = getContext("grid") + const { definition, rows } = getContext("grid") -{#if $table} +{#if $definition} rows.actions.refreshData()} /> {/if} diff --git a/packages/builder/src/components/backend/DataTable/modals/grid/GridCreateColumnModal.svelte b/packages/builder/src/components/backend/DataTable/modals/grid/GridCreateColumnModal.svelte index 56ce7ebc2b..2040f66706 100644 --- a/packages/builder/src/components/backend/DataTable/modals/grid/GridCreateColumnModal.svelte +++ b/packages/builder/src/components/backend/DataTable/modals/grid/GridCreateColumnModal.svelte @@ -2,7 +2,7 @@ import { getContext } from "svelte" import CreateEditColumn from "components/backend/DataTable/modals/CreateEditColumn.svelte" - const { rows } = getContext("grid") + const { datasource } = getContext("grid") - + diff --git a/packages/frontend-core/src/components/grid/controls/SizeButton.svelte b/packages/frontend-core/src/components/grid/controls/SizeButton.svelte index 22e0c6c2e9..c2797ce537 100644 --- a/packages/frontend-core/src/components/grid/controls/SizeButton.svelte +++ b/packages/frontend-core/src/components/grid/controls/SizeButton.svelte @@ -8,8 +8,14 @@ SmallRowHeight, } from "../lib/constants" - const { stickyColumn, columns, rowHeight, table, fixedRowHeight } = - getContext("grid") + const { + stickyColumn, + columns, + rowHeight, + definition, + fixedRowHeight, + datasource, + } = getContext("grid") // Some constants for column width options const smallColSize = 120 @@ -60,8 +66,8 @@ ] const changeRowHeight = height => { - columns.actions.saveTable({ - ...$table, + datasource.actions.saveDefinition({ + ...$definition, rowHeight: height, }) } diff --git a/packages/frontend-core/src/components/grid/index.js b/packages/frontend-core/src/components/grid/index.js index 25747ec142..453e12967f 100644 --- a/packages/frontend-core/src/components/grid/index.js +++ b/packages/frontend-core/src/components/grid/index.js @@ -1 +1,2 @@ export { default as Grid } from "./layout/Grid.svelte" +export { DatasourceType } from "./lib/constants" diff --git a/packages/frontend-core/src/components/grid/lib/constants.js b/packages/frontend-core/src/components/grid/lib/constants.js index a6e6723463..de9fa6591c 100644 --- a/packages/frontend-core/src/components/grid/lib/constants.js +++ b/packages/frontend-core/src/components/grid/lib/constants.js @@ -1,3 +1,7 @@ +export const DatasourceType = { + Table: "table", + ViewV2: "viewV2", +} export const Padding = 246 export const MaxCellRenderHeight = 222 export const ScrollBarSize = 8 diff --git a/packages/frontend-core/src/components/grid/stores/columns.js b/packages/frontend-core/src/components/grid/stores/columns.js index 6573bc3937..8059bd98d7 100644 --- a/packages/frontend-core/src/components/grid/stores/columns.js +++ b/packages/frontend-core/src/components/grid/stores/columns.js @@ -69,8 +69,7 @@ export const deriveStores = context => { } export const createActions = context => { - const { table, columns, stickyColumn, API, dispatch, config, datasource } = - context + const { columns, stickyColumn, config, datasource, definition } = context // Checks if we have a certain column by name const hasColumn = column => { @@ -79,13 +78,13 @@ export const createActions = context => { return $columns.some(col => col.name === column) || $sticky?.name === column } - // Updates the tables primary display column + // Updates the datasources primary display column const changePrimaryDisplay = async column => { if (!get(config).canEditPrimaryDisplay) { return } - return await saveTable({ - ...get(table), + return await datasource.actions.saveDefinition({ + ...get(definition), primaryDisplay: column, }) } @@ -107,14 +106,14 @@ export const createActions = context => { await saveChanges() } - // Persists column changes by saving metadata against table schema + // Persists column changes by saving metadata against datasource schema const saveChanges = async () => { const $columns = get(columns) - const $table = get(table) + const $definition = get(definition) const $stickyColumn = get(stickyColumn) - const newSchema = cloneDeep($table.schema) + const newSchema = cloneDeep($definition.schema) - // Build new updated table schema + // Build new updated datasource schema Object.keys(newSchema).forEach(column => { // Respect order specified by columns const index = $columns.findIndex(x => x.name === column) @@ -134,28 +133,10 @@ export const createActions = context => { } }) - await saveTable({ ...$table, schema: newSchema }) - } - - const saveTable = async newTable => { - const $config = get(config) - const $datasource = get(datasource) - - // Update local state - table.set(newTable) - - // Update server - if ($config.canSaveSchema) { - if ($datasource.type === "table") { - await API.saveTable(newTable) - } else if ($datasource.type === "viewV2") { - await API.viewV2.update({ ...newTable }) - } - } - - // Broadcast change to external state can be updated, as this change - // will not be received by the builder websocket because we caused it ourselves - dispatch("updatetable", newTable) + await datasource.actions.saveDefinition({ + ...$definition, + schema: newSchema, + }) } return { @@ -164,7 +145,6 @@ export const createActions = context => { actions: { hasColumn, saveChanges, - saveTable, changePrimaryDisplay, changeAllColumnWidths, }, @@ -173,51 +153,7 @@ export const createActions = context => { } export const initialise = context => { - const { table, columns, stickyColumn, schemaOverrides, columnWhitelist } = - context - - const schema = derived( - [table, schemaOverrides, columnWhitelist], - ([$table, $schemaOverrides, $columnWhitelist]) => { - if (!$table?.schema) { - return null - } - let newSchema = { ...$table?.schema } - - // Edge case to temporarily allow deletion of duplicated user - // fields that were saved with the "disabled" flag set. - // By overriding the saved schema we ensure only overrides can - // set the disabled flag. - // TODO: remove in future - Object.keys(newSchema).forEach(field => { - newSchema[field] = { - ...newSchema[field], - disabled: false, - } - }) - - // Apply schema overrides - Object.keys($schemaOverrides || {}).forEach(field => { - if (newSchema[field]) { - newSchema[field] = { - ...newSchema[field], - ...$schemaOverrides[field], - } - } - }) - - // Apply whitelist if specified - if ($columnWhitelist?.length) { - Object.keys(newSchema).forEach(key => { - if (!$columnWhitelist.includes(key)) { - delete newSchema[key] - } - }) - } - - return newSchema - } - ) + const { definition, columns, stickyColumn, schema } = context // Merge new schema fields with existing schema in order to preserve widths schema.subscribe($schema => { @@ -226,12 +162,12 @@ export const initialise = context => { stickyColumn.set(null) return } - const $table = get(table) + const $definition = get(definition) // Find primary display let primaryDisplay - if ($table.primaryDisplay && $schema[$table.primaryDisplay]) { - primaryDisplay = $table.primaryDisplay + if ($definition.primaryDisplay && $schema[$definition.primaryDisplay]) { + primaryDisplay = $definition.primaryDisplay } // Get field list diff --git a/packages/frontend-core/src/components/grid/stores/config.js b/packages/frontend-core/src/components/grid/stores/config.js index a0b1be19da..de81a32ff5 100644 --- a/packages/frontend-core/src/components/grid/stores/config.js +++ b/packages/frontend-core/src/components/grid/stores/config.js @@ -1,5 +1,6 @@ import { derivedMemo } from "../../../utils" import { derived } from "svelte/store" +import { DatasourceType } from "../lib/constants" export const deriveStores = context => { const { props, hasNonAutoColumn } = context @@ -28,8 +29,7 @@ export const deriveStores = context => { } // Disable some features if we're editing a view - if ($props.datasource?.type === "viewV2") { - config.canEditPrimaryDisplay = false + if ($props.datasource?.type === DatasourceType.ViewV2) { config.canEditColumns = false } diff --git a/packages/frontend-core/src/components/grid/stores/datsource.js b/packages/frontend-core/src/components/grid/stores/datsource.js new file mode 100644 index 0000000000..a0f6e36811 --- /dev/null +++ b/packages/frontend-core/src/components/grid/stores/datsource.js @@ -0,0 +1,107 @@ +import { derived, get, writable } from "svelte/store" + +export const createStores = () => { + const definition = writable(null) + + return { + definition, + } +} + +export const deriveStores = context => { + const { definition, schemaOverrides, columnWhitelist } = context + + const schema = derived( + [definition, schemaOverrides, columnWhitelist], + ([$definition, $schemaOverrides, $columnWhitelist]) => { + if (!$definition?.schema) { + return null + } + let newSchema = { ...$definition?.schema } + + // Edge case to temporarily allow deletion of duplicated user + // fields that were saved with the "disabled" flag set. + // By overriding the saved schema we ensure only overrides can + // set the disabled flag. + // TODO: remove in future + Object.keys(newSchema).forEach(field => { + newSchema[field] = { + ...newSchema[field], + disabled: false, + } + }) + + // Apply schema overrides + Object.keys($schemaOverrides || {}).forEach(field => { + if (newSchema[field]) { + newSchema[field] = { + ...newSchema[field], + ...$schemaOverrides[field], + } + } + }) + + // Apply whitelist if specified + if ($columnWhitelist?.length) { + Object.keys(newSchema).forEach(key => { + if (!$columnWhitelist.includes(key)) { + delete newSchema[key] + } + }) + } + + return newSchema + } + ) + + return { + schema, + } +} + +export const createActions = context => { + const { datasource, definition, API, config, dispatch } = context + + // Refreshes the datasource definition + const refreshDefinition = async () => { + const $datasource = get(datasource) + if ($datasource.type === "table") { + definition.set(await API.fetchTableDefinition($datasource.tableId)) + } else if ($datasource.type === "viewV2") { + // const definition = await API.viewsV2.(get(tableId)) + // table.set(definition) + } + } + + // Saves the datasource definition + const saveDefinition = async newDefinition => { + const $config = get(config) + const $datasource = get(datasource) + + // Update local state + definition.set(newDefinition) + + // Update server + if ($config.canSaveSchema) { + if ($datasource.type === "table") { + await API.saveTable(newDefinition) + } else if ($datasource.type === "viewV2") { + await API.viewV2.update({ ...newDefinition }) + } + } + + // Broadcast change to external state can be updated, as this change + // will not be received by the builder websocket because we caused it ourselves + dispatch("updatedefinition", newDefinition) + } + + return { + datasource: { + ...datasource, + actions: { + refreshDefinition, + saveDefinition, + }, + }, + } +} diff --git a/packages/frontend-core/src/components/grid/stores/index.js b/packages/frontend-core/src/components/grid/stores/index.js index d287025342..70a9471991 100644 --- a/packages/frontend-core/src/components/grid/stores/index.js +++ b/packages/frontend-core/src/components/grid/stores/index.js @@ -15,13 +15,18 @@ import * as Config from "./config" import * as Sort from "./sort" import * as Filter from "./filter" import * as Notifications from "./notifications" +import * as Table from "./table" +import * as ViewV2 from "./viewV2" +import * as Datasource from "./datsource" const DependencyOrderedStores = [ + // Common stores Notifications, Sort, Filter, Bounds, Scroll, + Datasource, Columns, Rows, UI, @@ -34,6 +39,10 @@ const DependencyOrderedStores = [ Pagination, Clipboard, Config, + + // Datasource specific stores + Table, + ViewV2, ] export const attachStores = context => { diff --git a/packages/frontend-core/src/components/grid/stores/rows.js b/packages/frontend-core/src/components/grid/stores/rows.js index 3c33d92d25..dce4fca64b 100644 --- a/packages/frontend-core/src/components/grid/stores/rows.js +++ b/packages/frontend-core/src/components/grid/stores/rows.js @@ -7,13 +7,13 @@ const SuppressErrors = true export const createStores = () => { const rows = writable([]) - const table = writable(null) const loading = writable(false) const loaded = writable(false) const rowChangeCache = writable({}) const inProgressChanges = writable({}) const hasNextPage = writable(false) const error = writable(null) + const fetch = writable(null) // Generate a lookup map to quick find a row by ID const rowLookupMap = derived(rows, $rows => { @@ -51,8 +51,8 @@ export const createStores = () => { ...rows, subscribe: enrichedRows.subscribe, }, + fetch, rowLookupMap, - table, loaded, loading, rowChangeCache, @@ -66,7 +66,7 @@ export const createActions = context => { const { rows, rowLookupMap, - table, + definition, filter, loading, sort, @@ -82,14 +82,14 @@ export const createActions = context => { hasNextPage, error, notifications, + fetch, } = context const instanceLoaded = writable(false) - const fetch = writable(null) // Local cache of row IDs to speed up checking if a row exists let rowCacheMap = {} - // Reset everything when table ID changes + // Reset everything when datasource changes let unsubscribe = null let lastResetKey = null datasource.subscribe(async $datasource => { @@ -100,11 +100,11 @@ export const createActions = context => { loading.set(true) // Abandon if we don't have a valid datasource - if (!$datasource?.tableId) { + if (!$datasource) { return } - // Tick to allow other reactive logic to update stores when table ID changes + // Tick to allow other reactive logic to update stores when datasource changes // before proceeding. This allows us to wipe filters etc if needed. await tick() const $filter = get(filter) @@ -142,7 +142,7 @@ export const createActions = context => { const previousResetKey = lastResetKey lastResetKey = $fetch.resetKey - // If resetting rows due to a table change, wipe data and wait for + // If resetting rows due to a datasource change, wipe data and wait for // derived stores to compute. This prevents stale data being passed // to cells when we save the new schema. if (!$instanceLoaded && previousResetKey) { @@ -152,16 +152,17 @@ export const createActions = context => { // Reset state properties when dataset changes if (!$instanceLoaded || resetRows) { - table.set($fetch.definition) - sort.set({ - column: $fetch.sortColumn, - order: $fetch.sortOrder, - }) + definition.set($fetch.definition) + + // sort.set({ + // column: $fetch.sortColumn, + // order: $fetch.sortOrder, + // }) } // Reset scroll state when data changes if (!$instanceLoaded) { - // Reset both top and left for a new table ID + // Reset both top and left for a new datasource ID instanceLoaded.set(true) scroll.set({ top: 0, left: 0 }) } else if (resetRows) { @@ -169,8 +170,6 @@ export const createActions = context => { scroll.update(state => ({ ...state, top: 0 })) } - // For views we always update the filter to match the definition - // Process new rows handleNewRows($fetch.rows, resetRows) @@ -182,23 +181,6 @@ export const createActions = context => { fetch.set(newFetch) }) - // Update fetch when filter or sort config changes - filter.subscribe($filter => { - if (get(datasource)?.type === "table") { - get(fetch)?.update({ - filter: $filter, - }) - } - }) - sort.subscribe($sort => { - if (get(datasource)?.type === "table") { - get(fetch)?.update({ - sortOrder: $sort.order, - sortColumn: $sort.column, - }) - } - }) - // Gets a row by ID const getRow = id => { const index = get(rowLookupMap)[id] @@ -506,17 +488,6 @@ export const createActions = context => { get(fetch)?.nextPage() } - // Refreshes the schema of the data fetch subscription - const refreshDatasourceDefinition = async () => { - const $datasource = get(datasource) - if ($datasource.type === "table") { - table.set(await API.fetchTableDefinition($datasource.tableId)) - } else if ($datasource.type === "viewV2") { - // const definition = await API.viewsV2.(get(tableId)) - // table.set(definition) - } - } - // Checks if we have a row with a certain ID const hasRow = id => { if (id === NewRowID) { @@ -550,21 +521,7 @@ export const createActions = context => { refreshRow, replaceRow, refreshData, - refreshDatasourceDefinition, }, }, } } - -export const initialise = context => { - const { table, filter, datasource } = context - - // For views, always keep the UI for filter and sorting up to date with the - // latest view definition - table.subscribe($definition => { - if (!$definition || get(datasource)?.type !== "viewV2") { - return - } - filter.set($definition.query) - }) -} diff --git a/packages/frontend-core/src/components/grid/stores/sort.js b/packages/frontend-core/src/components/grid/stores/sort.js index 3beecd2c2a..098e126930 100644 --- a/packages/frontend-core/src/components/grid/stores/sort.js +++ b/packages/frontend-core/src/components/grid/stores/sort.js @@ -16,7 +16,8 @@ export const createStores = context => { } export const initialise = context => { - const { sort, initialSortColumn, initialSortOrder } = context + const { sort, initialSortColumn, initialSortOrder, table, datasource } = + context // Reset sort when initial sort props change initialSortColumn.subscribe(newSortColumn => { diff --git a/packages/frontend-core/src/components/grid/stores/table.js b/packages/frontend-core/src/components/grid/stores/table.js new file mode 100644 index 0000000000..bcac6df48f --- /dev/null +++ b/packages/frontend-core/src/components/grid/stores/table.js @@ -0,0 +1,24 @@ +import { get } from "svelte/store" + +export const initialise = context => { + const { datasource, fetch, filter, sort } = context + + // Update fetch when filter changes + filter.subscribe($filter => { + if (get(datasource)?.type === "table") { + get(fetch)?.update({ + filter: $filter, + }) + } + }) + + // Update fetch when sorting changes + sort.subscribe($sort => { + if (get(datasource)?.type === "table") { + get(fetch)?.update({ + sortOrder: $sort.order, + sortColumn: $sort.column, + }) + } + }) +} diff --git a/packages/frontend-core/src/components/grid/stores/ui.js b/packages/frontend-core/src/components/grid/stores/ui.js index e0c6fd4c9c..e10e96a722 100644 --- a/packages/frontend-core/src/components/grid/stores/ui.js +++ b/packages/frontend-core/src/components/grid/stores/ui.js @@ -131,7 +131,7 @@ export const initialise = context => { focusedCellId, selectedRows, hoveredRowId, - table, + definition, rowHeight, fixedRowHeight, } = context @@ -187,9 +187,9 @@ export const initialise = context => { }) // Pull row height from table as long as we don't have a fixed height - table.subscribe($table => { + definition.subscribe($definition => { if (!get(fixedRowHeight)) { - rowHeight.set($table?.rowHeight || DefaultRowHeight) + rowHeight.set($definition?.rowHeight || DefaultRowHeight) } }) @@ -198,7 +198,7 @@ export const initialise = context => { if (height) { rowHeight.set(height) } else { - rowHeight.set(get(table)?.rowHeight || DefaultRowHeight) + rowHeight.set(get(definition)?.rowHeight || DefaultRowHeight) } }) } diff --git a/packages/frontend-core/src/components/grid/stores/viewV2.js b/packages/frontend-core/src/components/grid/stores/viewV2.js new file mode 100644 index 0000000000..bf47513908 --- /dev/null +++ b/packages/frontend-core/src/components/grid/stores/viewV2.js @@ -0,0 +1,43 @@ +import { get } from "svelte/store" + +export const initialise = context => { + const { definition, datasource, sort, rows } = context + + // For views, keep sort state in line with the view definition + definition.subscribe($definition => { + if (!$definition || get(datasource)?.type !== "viewV2") { + return + } + const $sort = get(sort) + if ( + $definition.sort?.field !== $sort?.column || + $definition.sort?.order !== $sort?.order + ) { + sort.set({ + column: $definition.sort?.field, + order: $definition.sort?.order, + }) + } + }) + + // When sorting changes, ensure view definition is kept up to date + sort.subscribe(async $sort => { + const $view = get(definition) + if (!$view || get(datasource)?.type !== "viewV2") { + return + } + if ( + $sort?.column !== $view.sort?.field || + $sort?.order !== $view.sort?.order + ) { + await datasource.actions.saveDefinition({ + ...$view, + sort: { + field: $sort.column, + order: $sort.order, + }, + }) + await rows.actions.refreshData() + } + }) +} diff --git a/packages/frontend-core/src/fetch/DataFetch.js b/packages/frontend-core/src/fetch/DataFetch.js index ea1cfdde77..a9803747ae 100644 --- a/packages/frontend-core/src/fetch/DataFetch.js +++ b/packages/frontend-core/src/fetch/DataFetch.js @@ -116,8 +116,16 @@ export default class DataFetch { async getInitialData() { const { datasource, filter, paginate } = this.options - // Fetch datasource definition and determine feature flags + // Fetch datasource definition and extract filter and sort if configured const definition = await this.getDefinition(datasource) + if (definition?.sort?.field) { + this.options.sortColumn = definition.sort.field + } + if (definition?.sort?.order) { + this.options.sortOrder = definition.sort.order + } + + // Determine feature flags const features = this.determineFeatureFlags(definition) this.features = { supportsSearch: !!features?.supportsSearch, From 3e97e299bf4702bcbd63918cdd13d28308582aaf Mon Sep 17 00:00:00 2001 From: Andrew Kingston Date: Thu, 3 Aug 2023 11:27:33 +0100 Subject: [PATCH 042/145] Fix sorting for tables --- .../src/components/grid/stores/{datsource.js => datasource.js} | 2 +- packages/frontend-core/src/components/grid/stores/index.js | 2 +- packages/frontend-core/src/components/grid/stores/table.js | 2 ++ 3 files changed, 4 insertions(+), 2 deletions(-) rename packages/frontend-core/src/components/grid/stores/{datsource.js => datasource.js} (98%) diff --git a/packages/frontend-core/src/components/grid/stores/datsource.js b/packages/frontend-core/src/components/grid/stores/datasource.js similarity index 98% rename from packages/frontend-core/src/components/grid/stores/datsource.js rename to packages/frontend-core/src/components/grid/stores/datasource.js index a0f6e36811..fb033cd1f1 100644 --- a/packages/frontend-core/src/components/grid/stores/datsource.js +++ b/packages/frontend-core/src/components/grid/stores/datasource.js @@ -86,7 +86,7 @@ export const createActions = context => { if ($datasource.type === "table") { await API.saveTable(newDefinition) } else if ($datasource.type === "viewV2") { - await API.viewV2.update({ ...newDefinition }) + await API.viewV2.update(newDefinition) } } diff --git a/packages/frontend-core/src/components/grid/stores/index.js b/packages/frontend-core/src/components/grid/stores/index.js index 70a9471991..8104319f38 100644 --- a/packages/frontend-core/src/components/grid/stores/index.js +++ b/packages/frontend-core/src/components/grid/stores/index.js @@ -17,7 +17,7 @@ import * as Filter from "./filter" import * as Notifications from "./notifications" import * as Table from "./table" import * as ViewV2 from "./viewV2" -import * as Datasource from "./datsource" +import * as Datasource from "./datasource" const DependencyOrderedStores = [ // Common stores diff --git a/packages/frontend-core/src/components/grid/stores/table.js b/packages/frontend-core/src/components/grid/stores/table.js index bcac6df48f..59d351950a 100644 --- a/packages/frontend-core/src/components/grid/stores/table.js +++ b/packages/frontend-core/src/components/grid/stores/table.js @@ -15,6 +15,8 @@ export const initialise = context => { // Update fetch when sorting changes sort.subscribe($sort => { if (get(datasource)?.type === "table") { + console.log("update", $sort) + console.log(get(fetch)) get(fetch)?.update({ sortOrder: $sort.order, sortColumn: $sort.column, From b00f3d24188b6798c18ec4340b98f25f41ef5064 Mon Sep 17 00:00:00 2001 From: Andrew Kingston Date: Thu, 3 Aug 2023 11:28:02 +0100 Subject: [PATCH 043/145] Remove log --- packages/frontend-core/src/components/grid/stores/table.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/packages/frontend-core/src/components/grid/stores/table.js b/packages/frontend-core/src/components/grid/stores/table.js index 59d351950a..bcac6df48f 100644 --- a/packages/frontend-core/src/components/grid/stores/table.js +++ b/packages/frontend-core/src/components/grid/stores/table.js @@ -15,8 +15,6 @@ export const initialise = context => { // Update fetch when sorting changes sort.subscribe($sort => { if (get(datasource)?.type === "table") { - console.log("update", $sort) - console.log(get(fetch)) get(fetch)?.update({ sortOrder: $sort.order, sortColumn: $sort.column, From d443bf3616f97e0bc9f326ad40c74eeb31a24765 Mon Sep 17 00:00:00 2001 From: Andrew Kingston Date: Thu, 3 Aug 2023 13:18:58 +0100 Subject: [PATCH 044/145] Split out more datasource specific logic --- .../src/components/grid/index.js | 1 - .../src/components/grid/lib/constants.js | 4 -- .../src/components/grid/stores/config.js | 3 +- .../src/components/grid/stores/datasource.js | 64 ++++++++++--------- .../src/components/grid/stores/index.js | 7 +- .../src/components/grid/stores/rows.js | 45 +++---------- .../src/components/grid/stores/table.js | 38 +++++++++++ .../src/components/grid/stores/viewV2.js | 58 +++++++++++++++++ 8 files changed, 142 insertions(+), 78 deletions(-) diff --git a/packages/frontend-core/src/components/grid/index.js b/packages/frontend-core/src/components/grid/index.js index 453e12967f..25747ec142 100644 --- a/packages/frontend-core/src/components/grid/index.js +++ b/packages/frontend-core/src/components/grid/index.js @@ -1,2 +1 @@ export { default as Grid } from "./layout/Grid.svelte" -export { DatasourceType } from "./lib/constants" diff --git a/packages/frontend-core/src/components/grid/lib/constants.js b/packages/frontend-core/src/components/grid/lib/constants.js index de9fa6591c..a6e6723463 100644 --- a/packages/frontend-core/src/components/grid/lib/constants.js +++ b/packages/frontend-core/src/components/grid/lib/constants.js @@ -1,7 +1,3 @@ -export const DatasourceType = { - Table: "table", - ViewV2: "viewV2", -} export const Padding = 246 export const MaxCellRenderHeight = 222 export const ScrollBarSize = 8 diff --git a/packages/frontend-core/src/components/grid/stores/config.js b/packages/frontend-core/src/components/grid/stores/config.js index de81a32ff5..46bff299d4 100644 --- a/packages/frontend-core/src/components/grid/stores/config.js +++ b/packages/frontend-core/src/components/grid/stores/config.js @@ -1,6 +1,5 @@ import { derivedMemo } from "../../../utils" import { derived } from "svelte/store" -import { DatasourceType } from "../lib/constants" export const deriveStores = context => { const { props, hasNonAutoColumn } = context @@ -29,7 +28,7 @@ export const deriveStores = context => { } // Disable some features if we're editing a view - if ($props.datasource?.type === DatasourceType.ViewV2) { + if ($props.datasource?.type === "viewV2") { config.canEditColumns = false } diff --git a/packages/frontend-core/src/components/grid/stores/datasource.js b/packages/frontend-core/src/components/grid/stores/datasource.js index fb033cd1f1..4e6fbedfd6 100644 --- a/packages/frontend-core/src/components/grid/stores/datasource.js +++ b/packages/frontend-core/src/components/grid/stores/datasource.js @@ -19,18 +19,6 @@ export const deriveStores = context => { } let newSchema = { ...$definition?.schema } - // Edge case to temporarily allow deletion of duplicated user - // fields that were saved with the "disabled" flag set. - // By overriding the saved schema we ensure only overrides can - // set the disabled flag. - // TODO: remove in future - Object.keys(newSchema).forEach(field => { - newSchema[field] = { - ...newSchema[field], - disabled: false, - } - }) - // Apply schema overrides Object.keys($schemaOverrides || {}).forEach(field => { if (newSchema[field]) { @@ -60,34 +48,34 @@ export const deriveStores = context => { } export const createActions = context => { - const { datasource, definition, API, config, dispatch } = context + const { datasource, definition, config, dispatch, table, viewV2 } = context + + // Gets the appropriate API for the configured datasource type + const getAPI = () => { + const $datasource = get(datasource) + switch ($datasource?.type) { + case "table": + return table + case "viewV2": + return viewV2 + default: + return null + } + } // Refreshes the datasource definition const refreshDefinition = async () => { - const $datasource = get(datasource) - if ($datasource.type === "table") { - definition.set(await API.fetchTableDefinition($datasource.tableId)) - } else if ($datasource.type === "viewV2") { - // const definition = await API.viewsV2.(get(tableId)) - // table.set(definition) - } + return await getAPI()?.actions.refreshDefinition() } // Saves the datasource definition const saveDefinition = async newDefinition => { - const $config = get(config) - const $datasource = get(datasource) - // Update local state definition.set(newDefinition) // Update server - if ($config.canSaveSchema) { - if ($datasource.type === "table") { - await API.saveTable(newDefinition) - } else if ($datasource.type === "viewV2") { - await API.viewV2.update(newDefinition) - } + if (get(config).canSaveSchema) { + await getAPI()?.actions.saveDefinition(newDefinition) } // Broadcast change to external state can be updated, as this change @@ -95,12 +83,30 @@ export const createActions = context => { dispatch("updatedefinition", newDefinition) } + // Adds a row to the datasource + const addRow = async row => { + return await getAPI()?.actions.addRow(row) + } + + // Updates an existing row in the datasource + const updateRow = async row => { + return await getAPI()?.actions.updateRow(row) + } + + // Deletes rows from the datasource + const deleteRows = async rows => { + return await getAPI()?.actions.deleteRows(rows) + } + return { datasource: { ...datasource, actions: { refreshDefinition, saveDefinition, + addRow, + updateRow, + deleteRows, }, }, } diff --git a/packages/frontend-core/src/components/grid/stores/index.js b/packages/frontend-core/src/components/grid/stores/index.js index 8104319f38..cb858b7293 100644 --- a/packages/frontend-core/src/components/grid/stores/index.js +++ b/packages/frontend-core/src/components/grid/stores/index.js @@ -20,12 +20,13 @@ import * as ViewV2 from "./viewV2" import * as Datasource from "./datasource" const DependencyOrderedStores = [ - // Common stores Notifications, Sort, Filter, Bounds, Scroll, + Table, + ViewV2, Datasource, Columns, Rows, @@ -39,10 +40,6 @@ const DependencyOrderedStores = [ Pagination, Clipboard, Config, - - // Datasource specific stores - Table, - ViewV2, ] export const attachStores = context => { diff --git a/packages/frontend-core/src/components/grid/stores/rows.js b/packages/frontend-core/src/components/grid/stores/rows.js index dce4fca64b..d28707406b 100644 --- a/packages/frontend-core/src/components/grid/stores/rows.js +++ b/packages/frontend-core/src/components/grid/stores/rows.js @@ -240,19 +240,9 @@ export const createActions = context => { // Adds a new row const addRow = async (row, idx, bubble = false) => { try { - // Create row - const $datasource = get(datasource) + // Create row. Spread row so we can mutate and enrich safely. let newRow = { ...row } - if ($datasource.type === "table") { - newRow.tableId = $datasource.tableId - newRow = await API.saveRow(newRow, SuppressErrors) - } else if ($datasource.type === "viewV2") { - newRow.tableId = $datasource.tableId - newRow._viewId = $datasource.id - newRow = await API.viewV2.createRow(newRow) - } else { - return - } + newRow = await datasource.actions.addRow(newRow) // Update state if (idx != null) { @@ -381,19 +371,11 @@ export const createActions = context => { [rowId]: true, })) - let saved - if ($datasource.type === "table") { - saved = await API.saveRow( - { ...row, ...get(rowChangeCache)[rowId] }, - SuppressErrors - ) - } else if ($datasource.type === "viewV2") { - saved = await API.viewV2.updateRow( - { ...row, ...get(rowChangeCache)[rowId] }, - SuppressErrors - ) - saved._viewId = $datasource.id - } + // Update row + const saved = await datasource.actions.updateRow({ + ...row, + ...get(rowChangeCache)[rowId], + }) // Update state after a successful change if (saved?._id) { @@ -428,23 +410,12 @@ export const createActions = context => { if (!rowsToDelete?.length) { return } - const $datasource = get(datasource) // Actually delete rows rowsToDelete.forEach(row => { delete row.__idx }) - if ($datasource.type === "table") { - await API.deleteRows({ - tableId: $datasource.tableId, - rows: rowsToDelete, - }) - } else if ($datasource.type === "viewV2") { - await API.viewV2.deleteRows({ - viewId: $datasource.id, - rows: rowsToDelete, - }) - } + await datasource.actions.deleteRows(rowsToDelete) // Update state handleRemoveRows(rowsToDelete) diff --git a/packages/frontend-core/src/components/grid/stores/table.js b/packages/frontend-core/src/components/grid/stores/table.js index bcac6df48f..87a53343f4 100644 --- a/packages/frontend-core/src/components/grid/stores/table.js +++ b/packages/frontend-core/src/components/grid/stores/table.js @@ -1,5 +1,43 @@ import { get } from "svelte/store" +const SuppressErrors = true + +export const createActions = context => { + const { definition, API, datasource } = context + + const refreshDefinition = async () => { + definition.set(await API.fetchTableDefinition(get(datasource).tableId)) + } + + const saveDefinition = async newDefinition => { + await API.saveTable(newDefinition) + } + + const saveRow = async row => { + row.tableId = get(datasource)?.tableId + return await API.saveRow(row, SuppressErrors) + } + + const deleteRows = async rows => { + await API.deleteRows({ + tableId: get(datasource).tableId, + rows, + }) + } + + return { + table: { + actions: { + refreshDefinition, + saveDefinition, + addRow: saveRow, + updateRow: saveRow, + deleteRows, + }, + }, + } +} + export const initialise = context => { const { datasource, fetch, filter, sort } = context diff --git a/packages/frontend-core/src/components/grid/stores/viewV2.js b/packages/frontend-core/src/components/grid/stores/viewV2.js index bf47513908..44a73405a5 100644 --- a/packages/frontend-core/src/components/grid/stores/viewV2.js +++ b/packages/frontend-core/src/components/grid/stores/viewV2.js @@ -1,5 +1,63 @@ import { get } from "svelte/store" +const SuppressErrors = true + +export const createActions = context => { + const { definition, API, datasource } = context + + const refreshDefinition = async () => { + const $datasource = get(datasource) + if (!$datasource) { + definition.set(null) + return + } + const table = await API.fetchTableDefinition($datasource.tableId) + const view = Object.values(table?.views || {}).find( + view => view.id === $datasource.id + ) + definition.set(view) + } + + const saveDefinition = async newDefinition => { + await API.viewV2.update(newDefinition) + } + + const addRow = async row => { + const $datasource = get(datasource) + row.tableId = $datasource?.tableId + row._viewId = $datasource?.id + return await API.viewV2.createRow(row, SuppressErrors) + } + + const updateRow = async row => { + const $datasource = get(datasource) + const savedRow = await API.viewV2.updateRow(row, SuppressErrors) + return { + ...savedRow, + _viewId: $datasource.id, + } + } + + const deleteRows = async rows => { + await API.viewV2.deleteRows({ + viewId: get(datasource).id, + rows, + }) + } + + return { + viewV2: { + actions: { + refreshDefinition, + saveDefinition, + addRow, + updateRow, + deleteRows, + }, + }, + } +} + export const initialise = context => { const { definition, datasource, sort, rows } = context From 3eeb945934c58f5be69c5c11f6e1a13bfac72b30 Mon Sep 17 00:00:00 2001 From: Andrew Kingston Date: Thu, 3 Aug 2023 13:22:49 +0100 Subject: [PATCH 045/145] Split out logic for getting rows from datasources --- .../src/components/grid/stores/datasource.js | 5 +++++ .../src/components/grid/stores/rows.js | 20 +------------------ .../src/components/grid/stores/table.js | 15 ++++++++++++++ .../src/components/grid/stores/viewV2.js | 16 +++++++++++++++ 4 files changed, 37 insertions(+), 19 deletions(-) diff --git a/packages/frontend-core/src/components/grid/stores/datasource.js b/packages/frontend-core/src/components/grid/stores/datasource.js index 4e6fbedfd6..5ace5ceef8 100644 --- a/packages/frontend-core/src/components/grid/stores/datasource.js +++ b/packages/frontend-core/src/components/grid/stores/datasource.js @@ -98,6 +98,10 @@ export const createActions = context => { return await getAPI()?.actions.deleteRows(rows) } + const getRow = async id => { + return await getAPI()?.actions.getRow(id) + } + return { datasource: { ...datasource, @@ -107,6 +111,7 @@ export const createActions = context => { addRow, updateRow, deleteRows, + getRow, }, }, } diff --git a/packages/frontend-core/src/components/grid/stores/rows.js b/packages/frontend-core/src/components/grid/stores/rows.js index d28707406b..96194c6fc5 100644 --- a/packages/frontend-core/src/components/grid/stores/rows.js +++ b/packages/frontend-core/src/components/grid/stores/rows.js @@ -3,8 +3,6 @@ import { fetchData } from "../../../fetch/fetchData" import { NewRowID, RowPageSize } from "../lib/constants" import { tick } from "svelte" -const SuppressErrors = true - export const createStores = () => { const rows = writable([]) const loading = writable(false) @@ -280,21 +278,6 @@ export const createActions = context => { } } - // Fetches a row by ID using the search endpoint - const fetchRow = async id => { - const res = await API.searchTable({ - tableId: get(datasource).tableId, - limit: 1, - query: { - equal: { - _id: id, - }, - }, - paginate: false, - }) - return res?.rows?.[0] - } - // Replaces a row in state with the newly defined row, handling updates, // addition and deletion const replaceRow = (id, row) => { @@ -323,7 +306,7 @@ export const createActions = context => { // Refreshes a specific row const refreshRow = async id => { - const row = await fetchRow(id) + const row = await datasource.actions.getRow(id) replaceRow(id, row) } @@ -336,7 +319,6 @@ export const createActions = context => { const updateRow = async (rowId, changes) => { const $rows = get(rows) const $rowLookupMap = get(rowLookupMap) - const $datasource = get(datasource) const index = $rowLookupMap[rowId] const row = $rows[index] if (index == null || !Object.keys(changes || {}).length) { diff --git a/packages/frontend-core/src/components/grid/stores/table.js b/packages/frontend-core/src/components/grid/stores/table.js index 87a53343f4..d79696c263 100644 --- a/packages/frontend-core/src/components/grid/stores/table.js +++ b/packages/frontend-core/src/components/grid/stores/table.js @@ -25,6 +25,20 @@ export const createActions = context => { }) } + const getRow = async id => { + const res = await API.searchTable({ + tableId: get(datasource).tableId, + limit: 1, + query: { + equal: { + _id: id, + }, + }, + paginate: false, + }) + return res?.rows?.[0] + } + return { table: { actions: { @@ -33,6 +47,7 @@ export const createActions = context => { addRow: saveRow, updateRow: saveRow, deleteRows, + getRow, }, }, } diff --git a/packages/frontend-core/src/components/grid/stores/viewV2.js b/packages/frontend-core/src/components/grid/stores/viewV2.js index 44a73405a5..35e447d8fa 100644 --- a/packages/frontend-core/src/components/grid/stores/viewV2.js +++ b/packages/frontend-core/src/components/grid/stores/viewV2.js @@ -45,6 +45,21 @@ export const createActions = context => { }) } + // TODO: update in future. We can't depend on having table read access. + const getRow = async id => { + const res = await API.searchTable({ + tableId: get(datasource).tableId, + limit: 1, + query: { + equal: { + _id: id, + }, + }, + paginate: false, + }) + return res?.rows?.[0] + } + return { viewV2: { actions: { @@ -53,6 +68,7 @@ export const createActions = context => { addRow, updateRow, deleteRows, + getRow, }, }, } From f5e5a883cf82b9f386167027e5d551477ebb8bb8 Mon Sep 17 00:00:00 2001 From: Andrew Kingston Date: Thu, 3 Aug 2023 13:31:44 +0100 Subject: [PATCH 046/145] Enable renaming views and lint --- .../builder/src/stores/backend/viewsV2.js | 30 ++++++++----------- .../src/components/grid/stores/sort.js | 3 +- 2 files changed, 14 insertions(+), 19 deletions(-) diff --git a/packages/builder/src/stores/backend/viewsV2.js b/packages/builder/src/stores/backend/viewsV2.js index 8b7b1d876c..511545adcb 100644 --- a/packages/builder/src/stores/backend/viewsV2.js +++ b/packages/builder/src/stores/backend/viewsV2.js @@ -54,23 +54,20 @@ export function createViewsV2Store() { } const save = async view => { - // No dedicated save endpoint at this time - // const savedView = await API.saveView(view) - // - // // Update tables - // tables.update(state => { - // const table = state.list.find(table => table._id === view.tableId) - // if (table) { - // if (view.originalName) { - // delete table.views[view.originalName] - // } - // table.views[view.name] = savedView - // } - // return { ...state } - // }) - } + const savedView = await API.viewV2.update(view) - const replace = (id, view) => {} + // Update tables + tables.update(state => { + const table = state.list.find(table => table._id === view.tableId) + if (table) { + if (view.originalName) { + delete table.views[view.originalName] + } + table.views[view.name] = savedView + } + return { ...state } + }) + } return { subscribe: derivedStore.subscribe, @@ -78,7 +75,6 @@ export function createViewsV2Store() { delete: deleteView, create, save, - replace, } } diff --git a/packages/frontend-core/src/components/grid/stores/sort.js b/packages/frontend-core/src/components/grid/stores/sort.js index 098e126930..3beecd2c2a 100644 --- a/packages/frontend-core/src/components/grid/stores/sort.js +++ b/packages/frontend-core/src/components/grid/stores/sort.js @@ -16,8 +16,7 @@ export const createStores = context => { } export const initialise = context => { - const { sort, initialSortColumn, initialSortOrder, table, datasource } = - context + const { sort, initialSortColumn, initialSortOrder } = context // Reset sort when initial sort props change initialSortColumn.subscribe(newSortColumn => { From 19ca7e4a0a70241a70966de49171ae3e066c27e1 Mon Sep 17 00:00:00 2001 From: Andrew Kingston Date: Thu, 3 Aug 2023 13:40:36 +0100 Subject: [PATCH 047/145] Fix issue with viewV2 renaming --- packages/builder/src/stores/backend/viewsV2.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/builder/src/stores/backend/viewsV2.js b/packages/builder/src/stores/backend/viewsV2.js index 511545adcb..c8f38ff5e5 100644 --- a/packages/builder/src/stores/backend/viewsV2.js +++ b/packages/builder/src/stores/backend/viewsV2.js @@ -54,7 +54,8 @@ export function createViewsV2Store() { } const save = async view => { - const savedView = await API.viewV2.update(view) + const res = await API.viewV2.update(view) + const savedView = res?.data // Update tables tables.update(state => { From 46f16764dbcbb578a77cddcf9ad8a186351a831e Mon Sep 17 00:00:00 2001 From: Andrew Kingston Date: Fri, 4 Aug 2023 11:47:06 +0100 Subject: [PATCH 048/145] Fix initial sorting state for tables and memoize sorting store to avoid loops --- .../src/components/grid/stores/rows.js | 5 ----- .../src/components/grid/stores/sort.js | 5 +++-- .../src/components/grid/stores/table.js | 18 +++++++++++++++++- .../src/components/grid/stores/viewV2.js | 14 ++++---------- 4 files changed, 24 insertions(+), 18 deletions(-) diff --git a/packages/frontend-core/src/components/grid/stores/rows.js b/packages/frontend-core/src/components/grid/stores/rows.js index 96194c6fc5..d876b5f8df 100644 --- a/packages/frontend-core/src/components/grid/stores/rows.js +++ b/packages/frontend-core/src/components/grid/stores/rows.js @@ -151,11 +151,6 @@ export const createActions = context => { // Reset state properties when dataset changes if (!$instanceLoaded || resetRows) { definition.set($fetch.definition) - - // sort.set({ - // column: $fetch.sortColumn, - // order: $fetch.sortOrder, - // }) } // Reset scroll state when data changes diff --git a/packages/frontend-core/src/components/grid/stores/sort.js b/packages/frontend-core/src/components/grid/stores/sort.js index 3beecd2c2a..689b278874 100644 --- a/packages/frontend-core/src/components/grid/stores/sort.js +++ b/packages/frontend-core/src/components/grid/stores/sort.js @@ -1,11 +1,12 @@ -import { writable, get } from "svelte/store" +import { get } from "svelte/store" +import { memo } from "../../../utils" export const createStores = context => { const { props } = context const $props = get(props) // Initialise to default props - const sort = writable({ + const sort = memo({ column: $props.initialSortColumn, order: $props.initialSortOrder || "ascending", }) diff --git a/packages/frontend-core/src/components/grid/stores/table.js b/packages/frontend-core/src/components/grid/stores/table.js index d79696c263..5c3d2c9ef2 100644 --- a/packages/frontend-core/src/components/grid/stores/table.js +++ b/packages/frontend-core/src/components/grid/stores/table.js @@ -54,7 +54,7 @@ export const createActions = context => { } export const initialise = context => { - const { datasource, fetch, filter, sort } = context + const { datasource, fetch, filter, sort, definition } = context // Update fetch when filter changes filter.subscribe($filter => { @@ -74,4 +74,20 @@ export const initialise = context => { }) } }) + + // Ensure sorting UI reflects the fetch state whenever we reset the fetch, + // which triggers a new definition + definition.subscribe(() => { + if (get(datasource)?.type === "table") { + const $fetch = get(fetch) + if (!$fetch) { + return + } + const { sortColumn, sortOrder } = get($fetch) + sort.set({ + column: sortColumn, + order: sortOrder, + }) + } + }) } diff --git a/packages/frontend-core/src/components/grid/stores/viewV2.js b/packages/frontend-core/src/components/grid/stores/viewV2.js index 35e447d8fa..b2d80acd1a 100644 --- a/packages/frontend-core/src/components/grid/stores/viewV2.js +++ b/packages/frontend-core/src/components/grid/stores/viewV2.js @@ -82,16 +82,10 @@ export const initialise = context => { if (!$definition || get(datasource)?.type !== "viewV2") { return } - const $sort = get(sort) - if ( - $definition.sort?.field !== $sort?.column || - $definition.sort?.order !== $sort?.order - ) { - sort.set({ - column: $definition.sort?.field, - order: $definition.sort?.order, - }) - } + sort.set({ + column: $definition.sort?.field, + order: $definition.sort?.order, + }) }) // When sorting changes, ensure view definition is kept up to date From cd2231630f7d248dd249d99a8797989b76423266 Mon Sep 17 00:00:00 2001 From: Andrew Kingston Date: Fri, 4 Aug 2023 14:54:45 +0100 Subject: [PATCH 049/145] Improve updating of viewV2 state and restore grid<>builder sync for datasource definitions --- .../backend/DataTable/ViewV2DataTable.svelte | 3 +- .../builder/src/stores/backend/viewsV2.js | 73 ++++++++++++------- .../src/components/grid/stores/datasource.js | 2 +- 3 files changed, 50 insertions(+), 28 deletions(-) diff --git a/packages/builder/src/components/backend/DataTable/ViewV2DataTable.svelte b/packages/builder/src/components/backend/DataTable/ViewV2DataTable.svelte index 21b3e1c291..859ce11280 100644 --- a/packages/builder/src/components/backend/DataTable/ViewV2DataTable.svelte +++ b/packages/builder/src/components/backend/DataTable/ViewV2DataTable.svelte @@ -13,7 +13,8 @@ } const handleGridViewUpdate = async e => { - viewsV2.replace(id, e.detail) + console.log("update") + viewsV2.replaceView(id, e.detail) } diff --git a/packages/builder/src/stores/backend/viewsV2.js b/packages/builder/src/stores/backend/viewsV2.js index c8f38ff5e5..6fa3b52bb9 100644 --- a/packages/builder/src/stores/backend/viewsV2.js +++ b/packages/builder/src/stores/backend/viewsV2.js @@ -1,4 +1,4 @@ -import { writable, derived } from "svelte/store" +import { writable, derived, get } from "svelte/store" import { tables } from "./" import { API } from "api" @@ -30,44 +30,64 @@ export function createViewsV2Store() { const deleteView = async view => { await API.viewV2.delete(view.id) - - // Update tables - tables.update(state => { - const table = state.list.find(table => table._id === view.tableId) - delete table.views[view.name] - return { ...state } - }) + replaceView(view.id, null) } const create = async view => { const savedViewResponse = await API.viewV2.create(view) const savedView = savedViewResponse.data - - // Update tables - tables.update(state => { - const table = state.list.find(table => table._id === view.tableId) - table.views[view.name] = savedView - return { ...state } - }) - + replaceView(savedView.id, savedView) return savedView } const save = async view => { const res = await API.viewV2.update(view) const savedView = res?.data + replaceView(view.id, savedView) + } - // Update tables - tables.update(state => { - const table = state.list.find(table => table._id === view.tableId) - if (table) { - if (view.originalName) { - delete table.views[view.originalName] - } - table.views[view.name] = savedView - } - return { ...state } + // Handles external updates of tables + const replaceView = (viewId, view) => { + console.log("replace", viewId, view) + if (!viewId) { + return + } + const existingView = get(derivedStore).list.find(view => view.id === viewId) + const tableIndex = get(tables).list.findIndex(table => { + return table._id === view?.tableId || table._id === existingView?.tableId }) + if (tableIndex === -1) { + return + } + + // Handle deletion + if (!view) { + tables.update(state => { + delete state.list[tableIndex].views[existingView.name] + return state + }) + return + } + + // Add new view + if (!existingView) { + tables.update(state => { + state.list[tableIndex].views[view.name] = view + return state + }) + } + + // Update existing view + else { + tables.update(state => { + // Remove old view + delete state.list[tableIndex].views[existingView.name] + + // Add new view + state.list[tableIndex].views[view.name] = view + return state + }) + } } return { @@ -76,6 +96,7 @@ export function createViewsV2Store() { delete: deleteView, create, save, + replaceView, } } diff --git a/packages/frontend-core/src/components/grid/stores/datasource.js b/packages/frontend-core/src/components/grid/stores/datasource.js index 5ace5ceef8..d5017aa8d1 100644 --- a/packages/frontend-core/src/components/grid/stores/datasource.js +++ b/packages/frontend-core/src/components/grid/stores/datasource.js @@ -80,7 +80,7 @@ export const createActions = context => { // Broadcast change to external state can be updated, as this change // will not be received by the builder websocket because we caused it ourselves - dispatch("updatedefinition", newDefinition) + dispatch("updatedatasource", newDefinition) } // Adds a row to the datasource From 0a87e3502e2ad720d49facf102f04f062cd1d74d Mon Sep 17 00:00:00 2001 From: Andrew Kingston Date: Fri, 4 Aug 2023 14:58:12 +0100 Subject: [PATCH 050/145] Fix other user selection state for viewV2s --- .../components/backend/TableNavigator/TableNavigator.svelte | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/builder/src/components/backend/TableNavigator/TableNavigator.svelte b/packages/builder/src/components/backend/TableNavigator/TableNavigator.svelte index f1965117ae..056a36c4a7 100644 --- a/packages/builder/src/components/backend/TableNavigator/TableNavigator.svelte +++ b/packages/builder/src/components/backend/TableNavigator/TableNavigator.svelte @@ -58,7 +58,8 @@ $goto(`./view/v1/${encodeURIComponent(name)}`) } }} - selectedBy={$userSelectedResourceMap[name]} + selectedBy={$userSelectedResourceMap[name] || + $userSelectedResourceMap[view.id]} > From 938a5a445f87b32cc0672ba3feab58c3eb917eba Mon Sep 17 00:00:00 2001 From: Andrew Kingston Date: Fri, 4 Aug 2023 15:04:58 +0100 Subject: [PATCH 051/145] Add multi dev collab for viewV2 definitions --- packages/server/src/api/controllers/view/viewsV2.ts | 12 +++++++++++- packages/server/src/sdk/app/views/index.ts | 3 ++- 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/packages/server/src/api/controllers/view/viewsV2.ts b/packages/server/src/api/controllers/view/viewsV2.ts index 226b7201b8..ba97fff8b6 100644 --- a/packages/server/src/api/controllers/view/viewsV2.ts +++ b/packages/server/src/api/controllers/view/viewsV2.ts @@ -8,6 +8,7 @@ import { ViewV2, RequiredKeys, } from "@budibase/types" +import { builderSocket } from "../../../websockets" async function parseSchemaUI(ctx: Ctx, view: CreateViewRequest) { if (!view.schema) { @@ -86,6 +87,9 @@ export async function create(ctx: Ctx) { ctx.body = { data: result, } + + const table = await sdk.tables.getTable(tableId) + builderSocket?.emitTableUpdate(ctx, table) } export async function update(ctx: Ctx) { @@ -118,11 +122,17 @@ export async function update(ctx: Ctx) { ctx.body = { data: result, } + + const table = await sdk.tables.getTable(tableId) + builderSocket?.emitTableUpdate(ctx, table) } export async function remove(ctx: Ctx) { const { viewId } = ctx.params - await sdk.views.remove(viewId) + const view = await sdk.views.remove(viewId) ctx.status = 204 + + const table = await sdk.tables.getTable(view.tableId) + builderSocket?.emitTableUpdate(ctx, table) } diff --git a/packages/server/src/sdk/app/views/index.ts b/packages/server/src/sdk/app/views/index.ts index d87715f49b..aafaab3a36 100644 --- a/packages/server/src/sdk/app/views/index.ts +++ b/packages/server/src/sdk/app/views/index.ts @@ -56,7 +56,7 @@ export function isV2(view: View | ViewV2): view is ViewV2 { return (view as ViewV2).version === 2 } -export async function remove(viewId: string): Promise { +export async function remove(viewId: string): Promise { const db = context.getAppDB() const view = await get(viewId) @@ -67,6 +67,7 @@ export async function remove(viewId: string): Promise { delete table.views![view?.name] await db.put(table) + return view } export function enrichSchema(view: View | ViewV2, tableSchema: TableSchema) { From ffe82e18e993b7a402cfccdd6e9b319898bced77 Mon Sep 17 00:00:00 2001 From: Andrew Kingston Date: Fri, 4 Aug 2023 15:07:44 +0100 Subject: [PATCH 052/145] Remove log --- packages/builder/src/stores/backend/viewsV2.js | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/builder/src/stores/backend/viewsV2.js b/packages/builder/src/stores/backend/viewsV2.js index 6fa3b52bb9..dcc2c27152 100644 --- a/packages/builder/src/stores/backend/viewsV2.js +++ b/packages/builder/src/stores/backend/viewsV2.js @@ -48,7 +48,6 @@ export function createViewsV2Store() { // Handles external updates of tables const replaceView = (viewId, view) => { - console.log("replace", viewId, view) if (!viewId) { return } From 8a9db6d8deef8d27ec8909e3ef038e781a809951 Mon Sep 17 00:00:00 2001 From: Andrew Kingston Date: Fri, 4 Aug 2023 15:15:43 +0100 Subject: [PATCH 053/145] Remove log --- .../src/components/backend/DataTable/ViewV2DataTable.svelte | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/builder/src/components/backend/DataTable/ViewV2DataTable.svelte b/packages/builder/src/components/backend/DataTable/ViewV2DataTable.svelte index 859ce11280..e4e1adda36 100644 --- a/packages/builder/src/components/backend/DataTable/ViewV2DataTable.svelte +++ b/packages/builder/src/components/backend/DataTable/ViewV2DataTable.svelte @@ -13,7 +13,6 @@ } const handleGridViewUpdate = async e => { - console.log("update") viewsV2.replaceView(id, e.detail) } From ef4ff87d8a3aacd73d23ef61f43b5a175c47a2b7 Mon Sep 17 00:00:00 2001 From: Andrew Kingston Date: Fri, 4 Aug 2023 15:17:30 +0100 Subject: [PATCH 054/145] Remove filter button on views for now --- .../src/components/backend/DataTable/ViewV2DataTable.svelte | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/packages/builder/src/components/backend/DataTable/ViewV2DataTable.svelte b/packages/builder/src/components/backend/DataTable/ViewV2DataTable.svelte index e4e1adda36..789f7d730a 100644 --- a/packages/builder/src/components/backend/DataTable/ViewV2DataTable.svelte +++ b/packages/builder/src/components/backend/DataTable/ViewV2DataTable.svelte @@ -26,9 +26,7 @@ showAvatars={false} on:updatedatasource={handleGridViewUpdate} > - - - + From dd4b3047369d7b0d8fdaf5d0ab03eaa1303ac854 Mon Sep 17 00:00:00 2001 From: Andrew Kingston Date: Fri, 4 Aug 2023 15:20:30 +0100 Subject: [PATCH 055/145] Fix creating views --- .../buttons/grid/GridCreateViewButton.svelte | 4 ++-- .../GridCreateViewModal.svelte} | 11 +++++------ 2 files changed, 7 insertions(+), 8 deletions(-) rename packages/builder/src/components/backend/DataTable/modals/{CreateViewModal.svelte => grid/GridCreateViewModal.svelte} (82%) diff --git a/packages/builder/src/components/backend/DataTable/buttons/grid/GridCreateViewButton.svelte b/packages/builder/src/components/backend/DataTable/buttons/grid/GridCreateViewButton.svelte index 33c416d7ef..3244ce3277 100644 --- a/packages/builder/src/components/backend/DataTable/buttons/grid/GridCreateViewButton.svelte +++ b/packages/builder/src/components/backend/DataTable/buttons/grid/GridCreateViewButton.svelte @@ -1,7 +1,7 @@ - + {/if}
diff --git a/packages/frontend-core/src/components/grid/stores/pagination.js b/packages/frontend-core/src/components/grid/stores/pagination.js index c6a856e229..1dbea6e0d2 100644 --- a/packages/frontend-core/src/components/grid/stores/pagination.js +++ b/packages/frontend-core/src/components/grid/stores/pagination.js @@ -1,4 +1,4 @@ -import { derived } from "svelte/store" +import { derived, get } from "svelte/store" export const initialise = context => { const { scrolledRowCount, rows, visualRowCapacity } = context @@ -11,13 +11,12 @@ export const initialise = context => { [scrolledRowCount, rowCount, visualRowCapacity], ([$scrolledRowCount, $rowCount, $visualRowCapacity]) => { return Math.max(0, $rowCount - $scrolledRowCount - $visualRowCapacity) - }, - 100 + } ) // Fetch next page when fewer than 25 remaining rows to scroll remainingRows.subscribe(remaining => { - if (remaining < 25) { + if (remaining < 25 && get(rowCount)) { rows.actions.loadNextPage() } }) diff --git a/packages/frontend-core/src/components/grid/stores/sort.js b/packages/frontend-core/src/components/grid/stores/sort.js index 689b278874..9b2dbfb8df 100644 --- a/packages/frontend-core/src/components/grid/stores/sort.js +++ b/packages/frontend-core/src/components/grid/stores/sort.js @@ -1,4 +1,4 @@ -import { get } from "svelte/store" +import { derived, get } from "svelte/store" import { memo } from "../../../utils" export const createStores = context => { @@ -17,13 +17,34 @@ export const createStores = context => { } export const initialise = context => { - const { sort, initialSortColumn, initialSortOrder } = context + const { sort, initialSortColumn, initialSortOrder, definition } = context // Reset sort when initial sort props change initialSortColumn.subscribe(newSortColumn => { sort.update(state => ({ ...state, column: newSortColumn })) }) initialSortOrder.subscribe(newSortOrder => { - sort.update(state => ({ ...state, order: newSortOrder })) + sort.update(state => ({ ...state, order: newSortOrder || "ascending" })) + }) + + // Derive if the current sort column exists in the schema + const sortColumnExists = derived( + [sort, definition], + ([$sort, $definition]) => { + if (!$sort?.column) { + return true + } + return $definition?.schema?.[$sort.column] != null + } + ) + + // Clear sort state if our sort column does not exist + sortColumnExists.subscribe(exists => { + if (!exists) { + sort.set({ + column: null, + order: "ascending", + }) + } }) } diff --git a/packages/frontend-core/src/components/grid/stores/table.js b/packages/frontend-core/src/components/grid/stores/table.js index 5c33e8171d..6c877e353a 100644 --- a/packages/frontend-core/src/components/grid/stores/table.js +++ b/packages/frontend-core/src/components/grid/stores/table.js @@ -59,51 +59,55 @@ export const createActions = context => { } export const initialise = context => { - const { datasource, fetch, filter, sort, definition } = context + const { datasource, fetch, filter, sort, table } = context - // Wipe filter whenever table ID changes to avoid using stale filters + // Keep a list of subscriptions so that we can clear them when the datasource + // config changes + let unsubscribers = [] + + // Observe datasource changes and apply logic for table datasources datasource.subscribe($datasource => { - if ($datasource?.type !== "table") { + // Clear previous subscriptions + unsubscribers?.forEach(unsubscribe => unsubscribe()) + unsubscribers = [] + if (!table.actions.isDatasourceValid($datasource)) { return } + + // Wipe state filter.set([]) - }) - - // Update fetch when filter changes - filter.subscribe($filter => { - if (get(datasource)?.type !== "table") { - return - } - get(fetch)?.update({ - filter: $filter, - }) - }) - - // Update fetch when sorting changes - sort.subscribe($sort => { - if (get(datasource)?.type !== "table") { - return - } - get(fetch)?.update({ - sortOrder: $sort.order, - sortColumn: $sort.column, - }) - }) - - // Ensure sorting UI reflects the fetch state whenever we reset the fetch, - // which triggers a new definition - definition.subscribe(() => { - if (get(datasource)?.type !== "table") { - return - } - const $fetch = get(fetch) - if (!$fetch) { - return - } - const { sortColumn, sortOrder } = get($fetch) sort.set({ - column: sortColumn, - order: sortOrder, + column: null, + order: "ascending", }) + + // Update fetch when filter changes + unsubscribers.push( + filter.subscribe($filter => { + // Ensure we're updating the correct fetch + const $fetch = get(fetch) + if ($fetch?.options?.datasource?.tableId !== $datasource.tableId) { + return + } + $fetch.update({ + filter: $filter, + }) + }) + ) + + // Update fetch when sorting changes + unsubscribers.push( + sort.subscribe($sort => { + // Ensure we're updating the correct fetch + const $fetch = get(fetch) + if ($fetch?.options?.datasource?.tableId !== $datasource.tableId) { + return + } + $fetch.update({ + sortOrder: $sort.order || "ascending", + sortColumn: $sort.column, + }) + }) + ) }) } diff --git a/packages/frontend-core/src/components/grid/stores/viewV2.js b/packages/frontend-core/src/components/grid/stores/viewV2.js index b24bf56c98..f93dc57b69 100644 --- a/packages/frontend-core/src/components/grid/stores/viewV2.js +++ b/packages/frontend-core/src/components/grid/stores/viewV2.js @@ -69,62 +69,91 @@ export const createActions = context => { } export const initialise = context => { - const { definition, datasource, sort, rows, filter, subscribe } = context + const { definition, datasource, sort, rows, filter, subscribe, viewV2 } = + context - // Keep sort and filter state in line with the view definition - definition.subscribe($definition => { - if (!$definition || get(datasource)?.type !== "viewV2") { + // Keep a list of subscriptions so that we can clear them when the datasource + // config changes + let unsubscribers = [] + + // Observe datasource changes and apply logic for view V2 datasources + datasource.subscribe($datasource => { + // Clear previous subscriptions + unsubscribers?.forEach(unsubscribe => unsubscribe()) + unsubscribers = [] + if (!viewV2.actions.isDatasourceValid($datasource)) { return } + + // Reset state for new view + filter.set([]) sort.set({ - column: $definition.sort?.field, - order: $definition.sort?.order, + column: null, + order: "ascending", }) - filter.set($definition.query || []) - }) - // When sorting changes, ensure view definition is kept up to date - sort.subscribe(async $sort => { - const $view = get(definition) - if (!$view || get(datasource)?.type !== "viewV2") { - return - } - if ( - $sort?.column !== $view.sort?.field || - $sort?.order !== $view.sort?.order - ) { - await datasource.actions.saveDefinition({ - ...$view, - sort: { - field: $sort.column, - order: $sort.order, - }, + // Keep sort and filter state in line with the view definition + unsubscribers.push( + definition.subscribe($definition => { + if ($definition?.id !== $datasource.id) { + return + } + sort.set({ + column: $definition.sort?.field, + order: $definition.sort?.order || "ascending", + }) + filter.set($definition.query || []) }) - await rows.actions.refreshData() - } - }) + ) - // When filters change, ensure view definition is kept up to date - filter.subscribe(async $filter => { - const $view = get(definition) - if (!$view || get(datasource)?.type !== "viewV2") { - return - } - if (JSON.stringify($filter) !== JSON.stringify($view.query)) { - await datasource.actions.saveDefinition({ - ...$view, - query: $filter, + // When sorting changes, ensure view definition is kept up to date + unsubscribers.push( + sort.subscribe(async $sort => { + // Ensure we're updating the correct view + const $view = get(definition) + if ($view?.id !== $datasource.id) { + return + } + if ( + $sort?.column !== $view.sort?.field || + $sort?.order !== $view.sort?.order + ) { + await datasource.actions.saveDefinition({ + ...$view, + sort: { + field: $sort.column, + order: $sort.order || "ascending", + }, + }) + await rows.actions.refreshData() + } }) - await rows.actions.refreshData() - } - }) + ) - // When hidden we show columns, we need to refresh data in order to fetch - // values for those columns - subscribe("show-column", async () => { - if (get(datasource)?.type !== "viewV2") { - return - } - await rows.actions.refreshData() + // When filters change, ensure view definition is kept up to date + unsubscribers?.push( + filter.subscribe(async $filter => { + // Ensure we're updating the correct view + const $view = get(definition) + if ($view?.id !== $datasource.id) { + return + } + if (JSON.stringify($filter) !== JSON.stringify($view.query)) { + await datasource.actions.saveDefinition({ + ...$view, + query: $filter, + }) + await rows.actions.refreshData() + } + }) + ) + + // When hidden we show columns, we need to refresh data in order to fetch + // values for those columns + unsubscribers.push( + subscribe("show-column", async () => { + await rows.actions.refreshData() + }) + ) }) } diff --git a/packages/frontend-core/src/fetch/DataFetch.js b/packages/frontend-core/src/fetch/DataFetch.js index 28fa68afe9..cd12535ddc 100644 --- a/packages/frontend-core/src/fetch/DataFetch.js +++ b/packages/frontend-core/src/fetch/DataFetch.js @@ -110,6 +110,17 @@ export default class DataFetch { return this.derivedStore.subscribe } + /** + * Gets the default sort column for this datasource + */ + getDefaultSortColumn(definition, schema) { + if (definition?.primaryDisplay && schema[definition.primaryDisplay]) { + return definition.primaryDisplay + } else { + return Object.keys(schema)[0] + } + } + /** * Fetches a fresh set of data from the server, resetting pagination */ @@ -118,12 +129,6 @@ export default class DataFetch { // Fetch datasource definition and extract sort properties if configured const definition = await this.getDefinition(datasource) - if (definition?.sort?.field) { - this.options.sortColumn = definition.sort.field - } - if (definition?.sort?.order) { - this.options.sortOrder = definition.sort.order - } // Determine feature flags const features = this.determineFeatureFlags(definition) @@ -140,32 +145,32 @@ export default class DataFetch { return } - // If no sort order, default to descending - if (!this.options.sortOrder) { + // If an invalid sort column is specified, delete it + if (this.options.sortColumn && !schema[this.options.sortColumn]) { + this.options.sortColumn = null + } + + // If no sort column, get the default column for this datasource + if (!this.options.sortColumn) { + this.options.sortColumn = this.getDefaultSortColumn(definition, schema) + } + + // If we don't have a sort column specified then just ensure we don't set + // any sorting params + if (!this.options.sortColumn) { this.options.sortOrder = "ascending" - } + this.options.sortType = null + } else { + // Otherwise determine what sort type to use base on sort column + const type = schema?.[this.options.sortColumn]?.type + this.options.sortType = + type === "number" || type === "bigint" ? "number" : "string" - // If no sort column, or an invalid sort column is provided, use the primary - // display and fallback to first column - const sortValid = this.options.sortColumn && schema[this.options.sortColumn] - if (!sortValid) { - let newSortColumn - if (definition?.primaryDisplay && schema[definition.primaryDisplay]) { - newSortColumn = definition.primaryDisplay - } else { - newSortColumn = Object.keys(schema)[0] + // If no sort order, default to ascending + if (!this.options.sortOrder) { + this.options.sortOrder = "ascending" } - this.options.sortColumn = newSortColumn } - const { sortOrder, sortColumn } = this.options - - // Determine what sort type to use - let sortType = "string" - if (sortColumn) { - const type = schema?.[sortColumn]?.type - sortType = type === "number" || type === "bigint" ? "number" : "string" - } - this.options.sortType = sortType // Build the lucene query let query = this.options.query @@ -182,8 +187,6 @@ export default class DataFetch { loading: true, cursors: [], cursor: null, - sortOrder, - sortColumn, })) // Actually fetch data @@ -351,6 +354,14 @@ export default class DataFetch { const entries = Object.entries(newOptions || {}) for (let [key, value] of entries) { if (JSON.stringify(value) !== JSON.stringify(this.options[key])) { + console.log( + key, + "is different", + "new", + value, + "vs old", + this.options[key] + ) refresh = true break } diff --git a/packages/frontend-core/src/fetch/QueryFetch.js b/packages/frontend-core/src/fetch/QueryFetch.js index 456abaec79..6420893515 100644 --- a/packages/frontend-core/src/fetch/QueryFetch.js +++ b/packages/frontend-core/src/fetch/QueryFetch.js @@ -29,6 +29,10 @@ export default class QueryFetch extends DataFetch { } } + getDefaultSortColumn() { + return null + } + async getData() { const { datasource, limit, paginate } = this.options const { supportsPagination } = this.features diff --git a/packages/frontend-core/src/fetch/ViewV2Fetch.js b/packages/frontend-core/src/fetch/ViewV2Fetch.js index d8b96df7ae..b9eaf4bdf7 100644 --- a/packages/frontend-core/src/fetch/ViewV2Fetch.js +++ b/packages/frontend-core/src/fetch/ViewV2Fetch.js @@ -4,9 +4,6 @@ import { get } from "svelte/store" export default class ViewV2Fetch extends DataFetch { determineFeatureFlags() { return { - // The API does not actually support dynamic filtering, but since views - // have filters built in we don't want to perform client side filtering - // which would happen if we marked this as false supportsSearch: true, supportsSort: true, supportsPagination: true, @@ -33,18 +30,23 @@ export default class ViewV2Fetch extends DataFetch { } } + getDefaultSortColumn() { + return null + } + async getData() { const { datasource, limit, sortColumn, sortOrder, sortType, paginate } = this.options - const { cursor } = get(this.store) + const { cursor, query } = get(this.store) try { const res = await this.API.viewV2.fetch({ viewId: datasource.id, + query, paginate, limit, bookmark: cursor, sort: sortColumn, - sortOrder, + sortOrder: sortOrder?.toLowerCase(), sortType, }) return { diff --git a/packages/server/src/api/controllers/row/views.ts b/packages/server/src/api/controllers/row/views.ts index 455a4c0aa2..578c775a55 100644 --- a/packages/server/src/api/controllers/row/views.ts +++ b/packages/server/src/api/controllers/row/views.ts @@ -6,9 +6,11 @@ import { SearchViewRowRequest, RequiredKeys, SearchParams, + SearchFilters, } from "@budibase/types" import { dataFilters } from "@budibase/shared-core" import sdk from "../../../sdk" +import { db } from "@budibase/backend-core" export async function searchView( ctx: UserCtx @@ -19,15 +21,37 @@ export async function searchView( if (!view) { ctx.throw(404, `View ${viewId} not found`) } - if (view.version !== 2) { ctx.throw(400, `This method only supports viewsV2`) } const viewFields = Object.keys(view.schema || {}) - const { body } = ctx.request - const query = dataFilters.buildLuceneQuery(view.query || []) + + // Enrich saved query with ephemeral query params. + // We prevent searching on any fields that are saved as part of the query, as + // that could let users find rows they should not be allowed to access. + let query = dataFilters.buildLuceneQuery(view.query || []) + if (body.query) { + // Extract existing fields + const existingFields = + view.query + ?.filter(filter => filter.field) + .map(filter => db.removeKeyNumbering(filter.field)) || [] + + // Prevent using an "OR" search + delete body.query.allOr + + // Carry over filters for unused fields + Object.keys(body.query).forEach(key => { + const operator = key as keyof Omit + Object.keys(body.query[operator] || {}).forEach(field => { + if (!existingFields.includes(db.removeKeyNumbering(field))) { + query[operator]![field] = body.query[operator]![field] + } + }) + }) + } const searchOptions: RequiredKeys & RequiredKeys> = { diff --git a/packages/types/src/api/web/app/rows.ts b/packages/types/src/api/web/app/rows.ts index 2b51c7b203..a99ef0e837 100644 --- a/packages/types/src/api/web/app/rows.ts +++ b/packages/types/src/api/web/app/rows.ts @@ -16,7 +16,13 @@ export interface SearchRowRequest extends Omit {} export interface SearchViewRowRequest extends Pick< SearchRowRequest, - "sort" | "sortOrder" | "sortType" | "limit" | "bookmark" | "paginate" + | "sort" + | "sortOrder" + | "sortType" + | "limit" + | "bookmark" + | "paginate" + | "query" > {} export interface SearchRowResponse { From 729b93532b0fd9a65af497a914e350788f743dac Mon Sep 17 00:00:00 2001 From: Andrew Kingston Date: Mon, 21 Aug 2023 12:01:17 +0100 Subject: [PATCH 095/145] Prevent overriding onEmptyFilter behaviour for views --- packages/server/src/api/controllers/row/views.ts | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/packages/server/src/api/controllers/row/views.ts b/packages/server/src/api/controllers/row/views.ts index 578c775a55..36a0b588b6 100644 --- a/packages/server/src/api/controllers/row/views.ts +++ b/packages/server/src/api/controllers/row/views.ts @@ -39,12 +39,16 @@ export async function searchView( ?.filter(filter => filter.field) .map(filter => db.removeKeyNumbering(filter.field)) || [] - // Prevent using an "OR" search + // Delete extraneous search params that cannot be overridden delete body.query.allOr + delete body.query.onEmptyFilter // Carry over filters for unused fields Object.keys(body.query).forEach(key => { - const operator = key as keyof Omit + const operator = key as keyof Omit< + SearchFilters, + "allOr" | "onEmptyFilter" + > Object.keys(body.query[operator] || {}).forEach(field => { if (!existingFields.includes(db.removeKeyNumbering(field))) { query[operator]![field] = body.query[operator]![field] From 5abab4cb625a11b7b04fd00997e1dc4ed1ccf1cb Mon Sep 17 00:00:00 2001 From: Andrew Kingston Date: Mon, 21 Aug 2023 16:11:37 +0100 Subject: [PATCH 096/145] Update grids to allow filtering and sorting in client lib with V2 views --- .../src/components/grid/stores/table.js | 17 ++- .../src/components/grid/stores/viewV2.js | 115 ++++++++++++------ 2 files changed, 94 insertions(+), 38 deletions(-) diff --git a/packages/frontend-core/src/components/grid/stores/table.js b/packages/frontend-core/src/components/grid/stores/table.js index 6c877e353a..a881f45b5f 100644 --- a/packages/frontend-core/src/components/grid/stores/table.js +++ b/packages/frontend-core/src/components/grid/stores/table.js @@ -59,7 +59,16 @@ export const createActions = context => { } export const initialise = context => { - const { datasource, fetch, filter, sort, table } = context + const { + datasource, + fetch, + filter, + sort, + table, + initialFilter, + initialSortColumn, + initialSortOrder, + } = context // Keep a list of subscriptions so that we can clear them when the datasource // config changes @@ -75,10 +84,10 @@ export const initialise = context => { } // Wipe state - filter.set([]) + filter.set(get(initialFilter)) sort.set({ - column: null, - order: "ascending", + column: get(initialSortColumn), + order: get(initialSortOrder) || "ascending", }) // Update fetch when filter changes diff --git a/packages/frontend-core/src/components/grid/stores/viewV2.js b/packages/frontend-core/src/components/grid/stores/viewV2.js index f93dc57b69..59bf747330 100644 --- a/packages/frontend-core/src/components/grid/stores/viewV2.js +++ b/packages/frontend-core/src/components/grid/stores/viewV2.js @@ -69,8 +69,20 @@ export const createActions = context => { } export const initialise = context => { - const { definition, datasource, sort, rows, filter, subscribe, viewV2 } = - context + const { + definition, + datasource, + sort, + rows, + filter, + subscribe, + viewV2, + initialFilter, + initialSortColumn, + initialSortOrder, + config, + fetch, + } = context // Keep a list of subscriptions so that we can clear them when the datasource // config changes @@ -86,10 +98,10 @@ export const initialise = context => { } // Reset state for new view - filter.set([]) + filter.set(get(initialFilter)) sort.set({ - column: null, - order: "ascending", + column: get(initialSortColumn), + order: get(initialSortOrder) || "ascending", }) // Keep sort and filter state in line with the view definition @@ -98,34 +110,55 @@ export const initialise = context => { if ($definition?.id !== $datasource.id) { return } - sort.set({ - column: $definition.sort?.field, - order: $definition.sort?.order || "ascending", - }) - filter.set($definition.query || []) + // Only override sorting if we don't have an initial sort column + if (!get(initialSortColumn)) { + sort.set({ + column: $definition.sort?.field, + order: $definition.sort?.order || "ascending", + }) + } + // Only override filter state if we don't have an initial filter + if (!get(initialFilter)) { + filter.set($definition.query) + } }) ) // When sorting changes, ensure view definition is kept up to date unsubscribers.push( sort.subscribe(async $sort => { - // Ensure we're updating the correct view - const $view = get(definition) - if ($view?.id !== $datasource.id) { - return + // If we can mutate schema then update the view definition + if (get(config).canSaveSchema) { + // Ensure we're updating the correct view + const $view = get(definition) + if ($view?.id !== $datasource.id) { + return + } + if ( + $sort?.column !== $view.sort?.field || + $sort?.order !== $view.sort?.order + ) { + await datasource.actions.saveDefinition({ + ...$view, + sort: { + field: $sort.column, + order: $sort.order || "ascending", + }, + }) + await rows.actions.refreshData() + } } - if ( - $sort?.column !== $view.sort?.field || - $sort?.order !== $view.sort?.order - ) { - await datasource.actions.saveDefinition({ - ...$view, - sort: { - field: $sort.column, - order: $sort.order || "ascending", - }, + // Otherwise just update the fetch + else { + // Ensure we're updating the correct fetch + const $fetch = get(fetch) + if ($fetch?.options?.datasource?.tableId !== $datasource.tableId) { + return + } + $fetch.update({ + sortOrder: $sort.order || "ascending", + sortColumn: $sort.column, }) - await rows.actions.refreshData() } }) ) @@ -133,17 +166,31 @@ export const initialise = context => { // When filters change, ensure view definition is kept up to date unsubscribers?.push( filter.subscribe(async $filter => { - // Ensure we're updating the correct view - const $view = get(definition) - if ($view?.id !== $datasource.id) { - return + // If we can mutate schema then update the view definition + if (get(config).canSaveSchema) { + // Ensure we're updating the correct view + const $view = get(definition) + if ($view?.id !== $datasource.id) { + return + } + if (JSON.stringify($filter) !== JSON.stringify($view.query)) { + await datasource.actions.saveDefinition({ + ...$view, + query: $filter, + }) + await rows.actions.refreshData() + } } - if (JSON.stringify($filter) !== JSON.stringify($view.query)) { - await datasource.actions.saveDefinition({ - ...$view, - query: $filter, + // Otherwise just update the fetch + else { + // Ensure we're updating the correct fetch + const $fetch = get(fetch) + if ($fetch?.options?.datasource?.tableId !== $datasource.tableId) { + return + } + $fetch.update({ + filter: $filter, }) - await rows.actions.refreshData() } }) ) From 59559ee93c4e2b98cd18f1db891f785af9e43bfe Mon Sep 17 00:00:00 2001 From: Andrew Kingston Date: Mon, 21 Aug 2023 16:53:09 +0100 Subject: [PATCH 097/145] Remove log and account for onEmptyFilter in grid button --- .../backend/DataTable/buttons/TableFilterButton.svelte | 9 ++------- packages/frontend-core/src/fetch/DataFetch.js | 8 -------- 2 files changed, 2 insertions(+), 15 deletions(-) diff --git a/packages/builder/src/components/backend/DataTable/buttons/TableFilterButton.svelte b/packages/builder/src/components/backend/DataTable/buttons/TableFilterButton.svelte index b96738ab1a..23f6d1dea1 100644 --- a/packages/builder/src/components/backend/DataTable/buttons/TableFilterButton.svelte +++ b/packages/builder/src/components/backend/DataTable/buttons/TableFilterButton.svelte @@ -15,6 +15,7 @@ $: tempValue = filters || [] $: schemaFields = Object.values(schema || {}) $: text = getText(filters) + $: selected = tempValue.filter(x => !x.onEmptyFilter)?.length > 0 const getText = filters => { const count = filters?.filter(filter => filter.field)?.length @@ -22,13 +23,7 @@ } - 0} -> + {text} diff --git a/packages/frontend-core/src/fetch/DataFetch.js b/packages/frontend-core/src/fetch/DataFetch.js index cd12535ddc..857072601e 100644 --- a/packages/frontend-core/src/fetch/DataFetch.js +++ b/packages/frontend-core/src/fetch/DataFetch.js @@ -354,14 +354,6 @@ export default class DataFetch { const entries = Object.entries(newOptions || {}) for (let [key, value] of entries) { if (JSON.stringify(value) !== JSON.stringify(this.options[key])) { - console.log( - key, - "is different", - "new", - value, - "vs old", - this.options[key] - ) refresh = true break } From 10cbf4f08a887d17a570715673b09a9c456fb100 Mon Sep 17 00:00:00 2001 From: Andrew Kingston Date: Mon, 21 Aug 2023 17:49:21 +0100 Subject: [PATCH 098/145] Update grid sorting settings to make order conditional on column --- packages/client/manifest.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/client/manifest.json b/packages/client/manifest.json index 7be23cda77..cc3100acdf 100644 --- a/packages/client/manifest.json +++ b/packages/client/manifest.json @@ -5583,7 +5583,8 @@ "label": "Sort order", "key": "initialSortOrder", "options": ["Ascending", "Descending"], - "defaultValue": "Ascending" + "defaultValue": "Ascending", + "dependsOn": "initialSortColumn" }, { "type": "select", From c7d1010ce316695de8a37d0aaaf92641fe2d8dad Mon Sep 17 00:00:00 2001 From: Andrew Kingston Date: Mon, 21 Aug 2023 17:49:54 +0100 Subject: [PATCH 099/145] Fix resetting sort column when sort column doesn't exist in schema whenever schema is yet to be loaded --- packages/frontend-core/src/components/grid/stores/sort.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/frontend-core/src/components/grid/stores/sort.js b/packages/frontend-core/src/components/grid/stores/sort.js index 9b2dbfb8df..734a876eed 100644 --- a/packages/frontend-core/src/components/grid/stores/sort.js +++ b/packages/frontend-core/src/components/grid/stores/sort.js @@ -31,10 +31,10 @@ export const initialise = context => { const sortColumnExists = derived( [sort, definition], ([$sort, $definition]) => { - if (!$sort?.column) { + if (!$sort?.column || !$definition) { return true } - return $definition?.schema?.[$sort.column] != null + return $definition.schema?.[$sort.column] != null } ) From c936304410e44adad38091379a63a62b7540f4cb Mon Sep 17 00:00:00 2001 From: Andrew Kingston Date: Tue, 22 Aug 2023 11:31:25 +0100 Subject: [PATCH 100/145] Fix grid config store so that schema overrides work --- .../backend/DataTable/TableDataTable.svelte | 4 +-- .../components/grid/cells/HeaderCell.svelte | 1 - .../src/components/grid/stores/columns.js | 3 -- .../src/components/grid/stores/config.js | 36 ++++++++++--------- 4 files changed, 21 insertions(+), 23 deletions(-) diff --git a/packages/builder/src/components/backend/DataTable/TableDataTable.svelte b/packages/builder/src/components/backend/DataTable/TableDataTable.svelte index 109c965271..58e0a0e691 100644 --- a/packages/builder/src/components/backend/DataTable/TableDataTable.svelte +++ b/packages/builder/src/components/backend/DataTable/TableDataTable.svelte @@ -57,8 +57,8 @@ Use as display column diff --git a/packages/frontend-core/src/components/grid/stores/columns.js b/packages/frontend-core/src/components/grid/stores/columns.js index 8059bd98d7..09e25586fd 100644 --- a/packages/frontend-core/src/components/grid/stores/columns.js +++ b/packages/frontend-core/src/components/grid/stores/columns.js @@ -80,9 +80,6 @@ export const createActions = context => { // Updates the datasources primary display column const changePrimaryDisplay = async column => { - if (!get(config).canEditPrimaryDisplay) { - return - } return await datasource.actions.saveDefinition({ ...get(definition), primaryDisplay: column, diff --git a/packages/frontend-core/src/components/grid/stores/config.js b/packages/frontend-core/src/components/grid/stores/config.js index 46bff299d4..ae995b4ac7 100644 --- a/packages/frontend-core/src/components/grid/stores/config.js +++ b/packages/frontend-core/src/components/grid/stores/config.js @@ -1,8 +1,8 @@ import { derivedMemo } from "../../../utils" import { derived } from "svelte/store" -export const deriveStores = context => { - const { props, hasNonAutoColumn } = context +export const createStores = context => { + const { props } = context const getProp = prop => derivedMemo(props, $props => $props[prop]) // Derive and memoize some props so that we can react to them in isolation @@ -16,16 +16,27 @@ export const deriveStores = context => { const notifySuccess = getProp("notifySuccess") const notifyError = getProp("notifyError") + return { + datasource, + initialSortColumn, + initialSortOrder, + initialFilter, + fixedRowHeight, + schemaOverrides, + columnWhitelist, + notifySuccess, + notifyError, + } +} + +export const deriveStores = context => { + const { props, hasNonAutoColumn } = context + // Derive features const config = derived( [props, hasNonAutoColumn], ([$props, $hasNonAutoColumn]) => { - let config = { - ...$props, - - // Additional granular features which we don't expose as props - canEditPrimaryDisplay: $props.canEditColumns, - } + let config = { ...$props } // Disable some features if we're editing a view if ($props.datasource?.type === "viewV2") { @@ -43,14 +54,5 @@ export const deriveStores = context => { return { config, - datasource, - initialSortColumn, - initialSortOrder, - initialFilter, - fixedRowHeight, - schemaOverrides, - columnWhitelist, - notifySuccess, - notifyError, } } From eb7c12ba09b3817ca3c5fd63220d1533cb4ae701 Mon Sep 17 00:00:00 2001 From: Andrew Kingston Date: Tue, 22 Aug 2023 11:31:38 +0100 Subject: [PATCH 101/145] Prevent editing columns in grids in client apps --- packages/client/src/components/app/GridBlock.svelte | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/client/src/components/app/GridBlock.svelte b/packages/client/src/components/app/GridBlock.svelte index a7c3b8eef2..9bdea52124 100644 --- a/packages/client/src/components/app/GridBlock.svelte +++ b/packages/client/src/components/app/GridBlock.svelte @@ -50,6 +50,7 @@ canAddRows={allowAddRows} canEditRows={allowEditRows} canDeleteRows={allowDeleteRows} + canEditColumns={false} canExpandRows={false} canSaveSchema={false} showControls={false} From 51f6574942a8bc20dc50d4c7807fd3f8ef910344 Mon Sep 17 00:00:00 2001 From: Andrew Kingston Date: Tue, 22 Aug 2023 11:32:57 +0100 Subject: [PATCH 102/145] Fix issue with tab styles --- packages/bbui/src/Tabs/Tabs.svelte | 2 -- 1 file changed, 2 deletions(-) diff --git a/packages/bbui/src/Tabs/Tabs.svelte b/packages/bbui/src/Tabs/Tabs.svelte index 9c3d25a807..c94b396398 100644 --- a/packages/bbui/src/Tabs/Tabs.svelte +++ b/packages/bbui/src/Tabs/Tabs.svelte @@ -57,10 +57,8 @@ function calculateIndicatorLength() { if (!vertical) { width = $tab.info?.width + "px" - height = $tab.info?.height } else { height = $tab.info?.height + 4 + "px" - width = $tab.info?.width } } From e77644ce11ba0a4a9f8193b0989f4e651820b05f Mon Sep 17 00:00:00 2001 From: Andrew Kingston Date: Tue, 22 Aug 2023 11:48:06 +0100 Subject: [PATCH 103/145] Lint --- .../components/backend/DataTable/ViewV2DataTable.svelte | 2 +- .../DataTable/buttons/grid/GridManageAccessButton.svelte | 8 ++++---- .../frontend-core/src/components/grid/stores/columns.js | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/packages/builder/src/components/backend/DataTable/ViewV2DataTable.svelte b/packages/builder/src/components/backend/DataTable/ViewV2DataTable.svelte index 5cb65e340f..0c6a0cca9a 100644 --- a/packages/builder/src/components/backend/DataTable/ViewV2DataTable.svelte +++ b/packages/builder/src/components/backend/DataTable/ViewV2DataTable.svelte @@ -4,7 +4,7 @@ import { API } from "api" import GridCreateEditRowModal from "components/backend/DataTable/modals/grid/GridCreateEditRowModal.svelte" import GridFilterButton from "components/backend/DataTable/buttons/grid/GridFilterButton.svelte" - import GridManageAccessButton from "components/backend/DataTable/buttons/grid/GridManageAccessButton.svelte"; + import GridManageAccessButton from "components/backend/DataTable/buttons/grid/GridManageAccessButton.svelte" $: id = $viewsV2.selected?.id $: datasource = { diff --git a/packages/builder/src/components/backend/DataTable/buttons/grid/GridManageAccessButton.svelte b/packages/builder/src/components/backend/DataTable/buttons/grid/GridManageAccessButton.svelte index 8c7eb49923..0cd008bab1 100644 --- a/packages/builder/src/components/backend/DataTable/buttons/grid/GridManageAccessButton.svelte +++ b/packages/builder/src/components/backend/DataTable/buttons/grid/GridManageAccessButton.svelte @@ -7,10 +7,10 @@ $: resourceId = getResourceID($datasource) const getResourceID = datasource => { - if (!datasource) { - return null - } - return datasource.type === "table" ? datasource.tableId : datasource.id + if (!datasource) { + return null + } + return datasource.type === "table" ? datasource.tableId : datasource.id } diff --git a/packages/frontend-core/src/components/grid/stores/columns.js b/packages/frontend-core/src/components/grid/stores/columns.js index 09e25586fd..a5d90adcfe 100644 --- a/packages/frontend-core/src/components/grid/stores/columns.js +++ b/packages/frontend-core/src/components/grid/stores/columns.js @@ -69,7 +69,7 @@ export const deriveStores = context => { } export const createActions = context => { - const { columns, stickyColumn, config, datasource, definition } = context + const { columns, stickyColumn, datasource, definition } = context // Checks if we have a certain column by name const hasColumn = column => { From df676bbe9e55c8f1a42d61878f13fbe55c605308 Mon Sep 17 00:00:00 2001 From: Andrew Kingston Date: Tue, 22 Aug 2023 13:39:58 +0100 Subject: [PATCH 104/145] Update table block to work with views --- packages/client/src/components/app/blocks/TableBlock.svelte | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/client/src/components/app/blocks/TableBlock.svelte b/packages/client/src/components/app/blocks/TableBlock.svelte index efc83fd5ac..c2a13ddef2 100644 --- a/packages/client/src/components/app/blocks/TableBlock.svelte +++ b/packages/client/src/components/app/blocks/TableBlock.svelte @@ -53,7 +53,8 @@ $: editTitle = getEditTitle(detailsFormBlockId, primaryDisplay) $: normalFields = getNormalFields(schema) $: rowClickActions = - clickBehaviour === "actions" || dataSource?.type !== "table" + clickBehaviour === "actions" || + (dataSource?.type !== "table" && dataSource?.type !== "viewV2") ? onClick : [ { From 4192618bdf1df82da3239d86377d2b34343c12b5 Mon Sep 17 00:00:00 2001 From: Andrew Kingston Date: Wed, 23 Aug 2023 18:56:04 +0100 Subject: [PATCH 105/145] Handle missing required columns in views by showing errors --- .../src/components/grid/stores/columns.js | 8 -------- .../src/components/grid/stores/datasource.js | 6 ++++++ .../frontend-core/src/components/grid/stores/rows.js | 2 +- .../src/components/grid/stores/table.js | 9 ++++++++- .../src/components/grid/stores/viewV2.js | 12 +++++++++++- 5 files changed, 26 insertions(+), 11 deletions(-) diff --git a/packages/frontend-core/src/components/grid/stores/columns.js b/packages/frontend-core/src/components/grid/stores/columns.js index a5d90adcfe..629d5dd893 100644 --- a/packages/frontend-core/src/components/grid/stores/columns.js +++ b/packages/frontend-core/src/components/grid/stores/columns.js @@ -71,13 +71,6 @@ export const deriveStores = context => { export const createActions = context => { const { columns, stickyColumn, datasource, definition } = context - // Checks if we have a certain column by name - const hasColumn = column => { - const $columns = get(columns) - const $sticky = get(stickyColumn) - return $columns.some(col => col.name === column) || $sticky?.name === column - } - // Updates the datasources primary display column const changePrimaryDisplay = async column => { return await datasource.actions.saveDefinition({ @@ -140,7 +133,6 @@ export const createActions = context => { columns: { ...columns, actions: { - hasColumn, saveChanges, changePrimaryDisplay, changeAllColumnWidths, diff --git a/packages/frontend-core/src/components/grid/stores/datasource.js b/packages/frontend-core/src/components/grid/stores/datasource.js index 61caa79734..3f4347953e 100644 --- a/packages/frontend-core/src/components/grid/stores/datasource.js +++ b/packages/frontend-core/src/components/grid/stores/datasource.js @@ -108,6 +108,11 @@ export const createActions = context => { return getAPI()?.actions.isDatasourceValid(datasource) } + // Checks if this datasource can use a specific column by name + const canUseColumn = name => { + return getAPI()?.actions.canUseColumn(name) + } + return { datasource: { ...datasource, @@ -119,6 +124,7 @@ export const createActions = context => { deleteRows, getRow, isDatasourceValid, + canUseColumn, }, }, } diff --git a/packages/frontend-core/src/components/grid/stores/rows.js b/packages/frontend-core/src/components/grid/stores/rows.js index f595276e73..392bf392e8 100644 --- a/packages/frontend-core/src/components/grid/stores/rows.js +++ b/packages/frontend-core/src/components/grid/stores/rows.js @@ -192,7 +192,7 @@ export const createActions = context => { let erroredColumns = [] let missingColumns = [] for (let column of keys) { - if (columns.actions.hasColumn(column)) { + if (datasource.actions.canUseColumn(column)) { erroredColumns.push(column) } else { missingColumns.push(column) diff --git a/packages/frontend-core/src/components/grid/stores/table.js b/packages/frontend-core/src/components/grid/stores/table.js index a881f45b5f..ed13609f45 100644 --- a/packages/frontend-core/src/components/grid/stores/table.js +++ b/packages/frontend-core/src/components/grid/stores/table.js @@ -3,7 +3,7 @@ import { get } from "svelte/store" const SuppressErrors = true export const createActions = context => { - const { definition, API, datasource } = context + const { definition, API, datasource, columns, stickyColumn } = context const refreshDefinition = async () => { definition.set(await API.fetchTableDefinition(get(datasource).tableId)) @@ -43,6 +43,12 @@ export const createActions = context => { return res?.rows?.[0] } + const canUseColumn = name => { + const $columns = get(columns) + const $sticky = get(stickyColumn) + return $columns.some(col => col.name === name) || $sticky?.name === name + } + return { table: { actions: { @@ -53,6 +59,7 @@ export const createActions = context => { deleteRows, getRow, isDatasourceValid, + canUseColumn, }, }, } diff --git a/packages/frontend-core/src/components/grid/stores/viewV2.js b/packages/frontend-core/src/components/grid/stores/viewV2.js index 59bf747330..8f0e07c0de 100644 --- a/packages/frontend-core/src/components/grid/stores/viewV2.js +++ b/packages/frontend-core/src/components/grid/stores/viewV2.js @@ -3,7 +3,7 @@ import { get } from "svelte/store" const SuppressErrors = true export const createActions = context => { - const { definition, API, datasource } = context + const { definition, API, datasource, columns, stickyColumn } = context const refreshDefinition = async () => { const $datasource = get(datasource) @@ -53,6 +53,15 @@ export const createActions = context => { ) } + const canUseColumn = name => { + const $columns = get(columns) + const $sticky = get(stickyColumn) + return ( + $columns.some(col => col.name === name && col.visible) || + $sticky?.name === name + ) + } + return { viewV2: { actions: { @@ -63,6 +72,7 @@ export const createActions = context => { deleteRows, getRow, isDatasourceValid, + canUseColumn, }, }, } From 409df71c127f6c9258ab12af3efa7d31e73dc65b Mon Sep 17 00:00:00 2001 From: Andrew Kingston Date: Thu, 24 Aug 2023 12:11:22 +0100 Subject: [PATCH 106/145] Add hover color to divider in screen list --- .../design/[screenId]/_components/ScreenList/index.svelte | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/packages/builder/src/pages/builder/app/[application]/design/[screenId]/_components/ScreenList/index.svelte b/packages/builder/src/pages/builder/app/[application]/design/[screenId]/_components/ScreenList/index.svelte index c5475b9c16..ef5911c0f8 100644 --- a/packages/builder/src/pages/builder/app/[application]/design/[screenId]/_components/ScreenList/index.svelte +++ b/packages/builder/src/pages/builder/app/[application]/design/[screenId]/_components/ScreenList/index.svelte @@ -297,8 +297,12 @@ width: 100%; top: 50%; transform: translateY(-50%); + transition: background 130ms ease-out; } .divider:hover { cursor: row-resize; } + .divider:hover:after { + background: var(--spectrum-global-color-gray-300); + } From 50e3a66f92034d4999cfadced13ef59388e5fcca Mon Sep 17 00:00:00 2001 From: Andrew Kingston Date: Thu, 24 Aug 2023 12:26:46 +0100 Subject: [PATCH 107/145] Fix notifications in grid block in client apps --- packages/frontend-core/src/components/grid/stores/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/frontend-core/src/components/grid/stores/index.js b/packages/frontend-core/src/components/grid/stores/index.js index cb858b7293..7b73ea8be6 100644 --- a/packages/frontend-core/src/components/grid/stores/index.js +++ b/packages/frontend-core/src/components/grid/stores/index.js @@ -20,7 +20,6 @@ import * as ViewV2 from "./viewV2" import * as Datasource from "./datasource" const DependencyOrderedStores = [ - Notifications, Sort, Filter, Bounds, @@ -40,6 +39,7 @@ const DependencyOrderedStores = [ Pagination, Clipboard, Config, + Notifications, ] export const attachStores = context => { From b6e675e3ffc1f957ca457cc4874e0668a3641874 Mon Sep 17 00:00:00 2001 From: Andrew Kingston Date: Thu, 24 Aug 2023 15:15:15 +0100 Subject: [PATCH 108/145] Migrate DS+ settings without keys --- .../settings/controls/TableSelect.svelte | 21 +++++++++---------- .../src/components/app/DataProvider.svelte | 18 +++++++++++++++- 2 files changed, 27 insertions(+), 12 deletions(-) diff --git a/packages/builder/src/components/design/settings/controls/TableSelect.svelte b/packages/builder/src/components/design/settings/controls/TableSelect.svelte index 03b739e09f..ced1151969 100644 --- a/packages/builder/src/components/design/settings/controls/TableSelect.svelte +++ b/packages/builder/src/components/design/settings/controls/TableSelect.svelte @@ -1,6 +1,6 @@ table.name} - getOptionValue={table => table._id} + {options} + getOptionLabel={x => x.label} + getOptionValue={x => x.resourceId} /> diff --git a/packages/builder/src/components/design/settings/controls/TableSelect.svelte b/packages/builder/src/components/design/settings/controls/TableSelect.svelte index ced1151969..4a6f33202b 100644 --- a/packages/builder/src/components/design/settings/controls/TableSelect.svelte +++ b/packages/builder/src/components/design/settings/controls/TableSelect.svelte @@ -1,7 +1,7 @@
diff --git a/packages/builder/src/components/design/settings/controls/ButtonActionEditor/actions/DuplicateRow.svelte b/packages/builder/src/components/design/settings/controls/ButtonActionEditor/actions/DuplicateRow.svelte index bb253da56a..db1fff47f2 100644 --- a/packages/builder/src/components/design/settings/controls/ButtonActionEditor/actions/DuplicateRow.svelte +++ b/packages/builder/src/components/design/settings/controls/ButtonActionEditor/actions/DuplicateRow.svelte @@ -4,7 +4,7 @@ import { tables } from "stores/backend" import { getContextProviderComponents, - getSchemaForTable, + getSchemaForDatasourcePlus, } from "builderStore/dataBinding" import SaveFields from "./SaveFields.svelte" @@ -60,7 +60,7 @@ } const getSchemaFields = (asset, tableId) => { - const { schema } = getSchemaForTable(tableId) + const { schema } = getSchemaForDatasourcePlus(tableId) delete schema._id delete schema._rev return Object.values(schema || {}) diff --git a/packages/builder/src/components/design/settings/controls/ButtonActionEditor/actions/SaveRow.svelte b/packages/builder/src/components/design/settings/controls/ButtonActionEditor/actions/SaveRow.svelte index d16a279c68..c1917ad90f 100644 --- a/packages/builder/src/components/design/settings/controls/ButtonActionEditor/actions/SaveRow.svelte +++ b/packages/builder/src/components/design/settings/controls/ButtonActionEditor/actions/SaveRow.svelte @@ -1,10 +1,10 @@
- +