WIP: split views into separate stores, fix datasource list, use unique data table for views V2
This commit is contained in:
parent
25d993cb95
commit
150961fcf2
|
@ -26,6 +26,10 @@
|
|||
$: id = $tables.selected?._id
|
||||
$: isUsersTable = id === TableNames.USERS
|
||||
$: isInternal = $tables.selected?.type !== "external"
|
||||
$: datasource = {
|
||||
type: "table",
|
||||
tableId: id,
|
||||
}
|
||||
|
||||
const handleGridTableUpdate = async e => {
|
||||
tables.replaceTable(id, e.detail)
|
||||
|
@ -43,7 +47,7 @@
|
|||
<div class="wrapper">
|
||||
<Grid
|
||||
{API}
|
||||
tableId={id}
|
||||
{datasource}
|
||||
allowAddRows={!isUsersTable}
|
||||
allowDeleteRows={!isUsersTable}
|
||||
schemaOverrides={isUsersTable ? userSchemaOverrides : null}
|
|
@ -0,0 +1,50 @@
|
|||
<script>
|
||||
import { datasources, viewsV2 } from "stores/backend"
|
||||
import { Grid } from "@budibase/frontend-core"
|
||||
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 = {
|
||||
type: "viewV2",
|
||||
id,
|
||||
tableId: id?.split("_").slice(0, -1).join("_"),
|
||||
}
|
||||
$: console.log(datasource)
|
||||
|
||||
const handleGridViewUpdate = async e => {
|
||||
viewsV2.replace(id, e.detail)
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="wrapper">
|
||||
<Grid
|
||||
{API}
|
||||
{datasource}
|
||||
allowAddRows
|
||||
allowDeleteRows
|
||||
showAvatars={false}
|
||||
on:updatedatasource={handleGridViewUpdate}
|
||||
>
|
||||
<svelte:fragment slot="filter">
|
||||
<GridFilterButton />
|
||||
</svelte:fragment>
|
||||
<svelte:fragment slot="controls">
|
||||
<GridManageAccessButton />
|
||||
<GridCreateEditRowModal />
|
||||
</svelte:fragment>
|
||||
</Grid>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.wrapper {
|
||||
flex: 1 1 auto;
|
||||
margin: -28px -40px -40px -40px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
background: var(--background);
|
||||
overflow: hidden;
|
||||
}
|
||||
</style>
|
|
@ -1,7 +1,14 @@
|
|||
<script>
|
||||
import { goto, isActive, params } from "@roxi/routify"
|
||||
import { BUDIBASE_INTERNAL_DB_ID } from "constants/backend"
|
||||
import { database, datasources, queries, tables, views } from "stores/backend"
|
||||
import {
|
||||
database,
|
||||
datasources,
|
||||
queries,
|
||||
tables,
|
||||
views,
|
||||
viewsV2,
|
||||
} from "stores/backend"
|
||||
import EditDatasourcePopover from "./popovers/EditDatasourcePopover.svelte"
|
||||
import EditQueryPopover from "./popovers/EditQueryPopover.svelte"
|
||||
import NavItem from "components/common/NavItem.svelte"
|
||||
|
@ -24,6 +31,7 @@
|
|||
$tables,
|
||||
$queries,
|
||||
$views,
|
||||
$viewsV2,
|
||||
openDataSources
|
||||
)
|
||||
$: openDataSource = enrichedDataSources.find(x => x.open)
|
||||
|
@ -41,6 +49,7 @@
|
|||
tables,
|
||||
queries,
|
||||
views,
|
||||
viewsV2,
|
||||
openDataSources
|
||||
) => {
|
||||
if (!datasources?.list?.length) {
|
||||
|
@ -57,7 +66,8 @@
|
|||
isActive,
|
||||
tables,
|
||||
queries,
|
||||
views
|
||||
views,
|
||||
viewsV2
|
||||
)
|
||||
const onlySource = datasources.list.length === 1
|
||||
return {
|
||||
|
@ -106,7 +116,8 @@
|
|||
isActive,
|
||||
tables,
|
||||
queries,
|
||||
views
|
||||
views,
|
||||
viewsV2
|
||||
) => {
|
||||
// Check for being on a datasource page
|
||||
if (params.datasourceId === datasource._id) {
|
||||
|
@ -152,10 +163,19 @@
|
|||
|
||||
// Check for a matching view
|
||||
const selectedView = views.selected?.name
|
||||
const table = options.find(table => {
|
||||
const viewTable = options.find(table => {
|
||||
return table.views?.[selectedView] != null
|
||||
})
|
||||
return table != null
|
||||
if (viewTable) {
|
||||
return true
|
||||
}
|
||||
|
||||
// Check for a matching viewV2
|
||||
const selectedViewV2 = viewsV2.selected?.name
|
||||
const viewV2Table = options.find(table => {
|
||||
return table.views?.[selectedViewV2] != null
|
||||
})
|
||||
return viewV2Table != null
|
||||
}
|
||||
</script>
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
<script>
|
||||
import { tables, views, database } from "stores/backend"
|
||||
import { tables, views, viewsV2, database } from "stores/backend"
|
||||
import { TableNames } from "constants"
|
||||
import EditTablePopover from "./popovers/EditTablePopover.svelte"
|
||||
import EditViewPopover from "./popovers/EditViewPopover.svelte"
|
||||
|
@ -38,11 +38,15 @@
|
|||
{/if}
|
||||
</NavItem>
|
||||
{#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}
|
||||
<NavItem
|
||||
indentLevel={2}
|
||||
icon="Remove"
|
||||
text={viewName}
|
||||
selected={$isActive("./view") && $views.selected?.name === viewName}
|
||||
selected={viewSelected || viewV2Selected}
|
||||
on:click={() => {
|
||||
if (view.version === 2) {
|
||||
$goto(`./view/v2/${view.id}`)
|
||||
|
|
|
@ -51,7 +51,7 @@ export const syncURLToState = options => {
|
|||
let cachedRedirect = get(routify.redirect)
|
||||
let cachedPage = get(routify.page)
|
||||
let previousParamsHash = null
|
||||
let debug = false
|
||||
let debug = true
|
||||
const log = (...params) => debug && console.log(`[${urlParam}]`, ...params)
|
||||
|
||||
// Navigate to a certain URL
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
<script>
|
||||
import TableDataTable from "components/backend/DataTable/DataTable.svelte"
|
||||
import TableDataTable from "components/backend/DataTable/TableDataTable.svelte"
|
||||
import { tables, database } from "stores/backend"
|
||||
import { Banner } from "@budibase/bbui"
|
||||
|
||||
|
|
|
@ -5,8 +5,8 @@
|
|||
import { onDestroy } from "svelte"
|
||||
import { store } from "builderStore"
|
||||
|
||||
$: viewName = $views.selectedViewName
|
||||
$: store.actions.websocket.selectResource(viewName)
|
||||
$: name = $views.selectedViewName
|
||||
$: store.actions.websocket.selectResource(name)
|
||||
|
||||
const stopSyncing = syncURLToState({
|
||||
urlParam: "viewName",
|
||||
|
|
|
@ -1,23 +1,20 @@
|
|||
<script>
|
||||
import { views } from "stores/backend"
|
||||
import { viewsV2 } from "stores/backend"
|
||||
import { syncURLToState } from "helpers/urlStateSync"
|
||||
import * as routify from "@roxi/routify"
|
||||
import { onDestroy } from "svelte"
|
||||
import { store } from "builderStore"
|
||||
|
||||
$: viewName = $views.selectedViewName
|
||||
$: store.actions.websocket.selectResource(viewName)
|
||||
$: id = $viewsV2.selectedViewId
|
||||
$: store.actions.websocket.selectResource(id)
|
||||
|
||||
const stopSyncing = syncURLToState({
|
||||
urlParam: "id",
|
||||
stateKey: "selectedViewId",
|
||||
validate: id => $views.list?.some(view => view.id === id),
|
||||
update: id => {
|
||||
const view = $views.list.find(v => v.id === id)
|
||||
views.select(view.name)
|
||||
},
|
||||
validate: id => $viewsV2.list?.some(view => view.id === id),
|
||||
update: viewsV2.select,
|
||||
fallbackUrl: "../../",
|
||||
store: views,
|
||||
store: viewsV2,
|
||||
routify,
|
||||
decode: decodeURIComponent,
|
||||
})
|
||||
|
|
|
@ -1,28 +1,5 @@
|
|||
<script>
|
||||
import { API } from "api"
|
||||
import { Grid } from "@budibase/frontend-core"
|
||||
import { views } from "stores/backend"
|
||||
|
||||
$: selectedView = $views.selected
|
||||
import ViewV2DataTable from "components/backend/DataTable/ViewV2DataTable.svelte"
|
||||
</script>
|
||||
|
||||
<div class="wrapper">
|
||||
<Grid
|
||||
{API}
|
||||
tableId={selectedView?.id}
|
||||
datasourceType="viewV2"
|
||||
showAvatars={false}
|
||||
showControls={false}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.wrapper {
|
||||
flex: 1 1 auto;
|
||||
margin: -28px -40px -40px -40px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
background: var(--background);
|
||||
overflow: hidden;
|
||||
}
|
||||
</style>
|
||||
<ViewV2DataTable />
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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,
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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()
|
|
@ -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,
|
||||
|
|
|
@ -12,7 +12,7 @@
|
|||
width,
|
||||
config,
|
||||
hasNonAutoColumn,
|
||||
tableId,
|
||||
datasource,
|
||||
loading,
|
||||
} = getContext("grid")
|
||||
|
||||
|
@ -33,7 +33,7 @@
|
|||
</div>
|
||||
</GridScrollWrapper>
|
||||
{#if $config.allowSchemaChanges}
|
||||
{#key $tableId}
|
||||
{#key $datasource}
|
||||
<TempTooltip
|
||||
text="Click here to create your first column"
|
||||
type={TooltipType.Info}
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
dispatch,
|
||||
rows,
|
||||
focusedCellAPI,
|
||||
tableId,
|
||||
datasource,
|
||||
subscribe,
|
||||
renderedRows,
|
||||
renderedColumns,
|
||||
|
@ -38,7 +38,7 @@
|
|||
|
||||
$: firstColumn = $stickyColumn || $renderedColumns[0]
|
||||
$: width = GutterWidth + ($stickyColumn?.width || 0)
|
||||
$: $tableId, (visible = false)
|
||||
$: $datasource, (visible = false)
|
||||
$: invertY = shouldInvertY(offset, $rowVerticalInversionIndex, $renderedRows)
|
||||
$: selectedRowCount = Object.values($selectedRows).length
|
||||
$: hasNoRows = !$rows.length
|
||||
|
|
|
@ -3,18 +3,18 @@ import { createWebsocket } from "../../../utils"
|
|||
import { SocketEvent, GridSocketEvent } from "@budibase/shared-core"
|
||||
|
||||
export const createGridWebsocket = context => {
|
||||
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 => {
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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,
|
||||
|
|
Loading…
Reference in New Issue