Merge branch 'master' into feat/ts-store-conversions-pc
This commit is contained in:
commit
6658b923ae
|
@ -1,10 +1,54 @@
|
||||||
import { derived, get } from "svelte/store"
|
import { derived, get, Readable, Writable } from "svelte/store"
|
||||||
import { getDatasourceDefinition, getDatasourceSchema } from "../../../fetch"
|
import { getDatasourceDefinition, getDatasourceSchema } from "../../../fetch"
|
||||||
import { enrichSchemaWithRelColumns, memo } from "../../../utils"
|
import { enrichSchemaWithRelColumns, memo } from "../../../utils"
|
||||||
import { cloneDeep } from "lodash"
|
import { cloneDeep } from "lodash"
|
||||||
import { ViewV2Type } from "@budibase/types"
|
import {
|
||||||
|
Row,
|
||||||
|
SaveRowRequest,
|
||||||
|
SaveTableRequest,
|
||||||
|
UIDatasource,
|
||||||
|
UIFieldMutation,
|
||||||
|
UIFieldSchema,
|
||||||
|
UpdateViewRequest,
|
||||||
|
ViewV2Type,
|
||||||
|
} from "@budibase/types"
|
||||||
|
import { Store as StoreContext } from "."
|
||||||
|
import { DatasourceActions } from "./datasources"
|
||||||
|
|
||||||
export const createStores = () => {
|
interface DatasourceStore {
|
||||||
|
definition: Writable<UIDatasource>
|
||||||
|
schemaMutations: Writable<Record<string, UIFieldMutation>>
|
||||||
|
subSchemaMutations: Writable<Record<string, Record<string, UIFieldMutation>>>
|
||||||
|
}
|
||||||
|
|
||||||
|
interface DerivedDatasourceStore {
|
||||||
|
schema: Readable<Record<string, UIFieldSchema> | null>
|
||||||
|
enrichedSchema: Readable<Record<string, UIFieldSchema> | null>
|
||||||
|
hasBudibaseIdentifiers: Readable<boolean>
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ActionDatasourceStore {
|
||||||
|
datasource: DatasourceStore["definition"] & {
|
||||||
|
actions: DatasourceActions & {
|
||||||
|
refreshDefinition: () => Promise<void>
|
||||||
|
changePrimaryDisplay: (column: string) => Promise<void>
|
||||||
|
addSchemaMutation: (field: string, mutation: UIFieldMutation) => void
|
||||||
|
addSubSchemaMutation: (
|
||||||
|
field: string,
|
||||||
|
fromField: string,
|
||||||
|
mutation: UIFieldMutation
|
||||||
|
) => void
|
||||||
|
saveSchemaMutations: () => Promise<void>
|
||||||
|
resetSchemaMutations: () => void
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export type Store = DatasourceStore &
|
||||||
|
DerivedDatasourceStore &
|
||||||
|
ActionDatasourceStore
|
||||||
|
|
||||||
|
export const createStores = (): DatasourceStore => {
|
||||||
const definition = memo(null)
|
const definition = memo(null)
|
||||||
const schemaMutations = memo({})
|
const schemaMutations = memo({})
|
||||||
const subSchemaMutations = memo({})
|
const subSchemaMutations = memo({})
|
||||||
|
@ -16,7 +60,7 @@ export const createStores = () => {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export const deriveStores = context => {
|
export const deriveStores = (context: StoreContext): DerivedDatasourceStore => {
|
||||||
const {
|
const {
|
||||||
API,
|
API,
|
||||||
definition,
|
definition,
|
||||||
|
@ -27,7 +71,7 @@ export const deriveStores = context => {
|
||||||
} = context
|
} = context
|
||||||
|
|
||||||
const schema = derived(definition, $definition => {
|
const schema = derived(definition, $definition => {
|
||||||
let schema = getDatasourceSchema({
|
let schema: Record<string, UIFieldSchema> = getDatasourceSchema({
|
||||||
API,
|
API,
|
||||||
datasource: get(datasource),
|
datasource: get(datasource),
|
||||||
definition: $definition,
|
definition: $definition,
|
||||||
|
@ -40,7 +84,7 @@ export const deriveStores = context => {
|
||||||
// Certain datasources like queries use primitives.
|
// Certain datasources like queries use primitives.
|
||||||
Object.keys(schema || {}).forEach(key => {
|
Object.keys(schema || {}).forEach(key => {
|
||||||
if (typeof schema[key] !== "object") {
|
if (typeof schema[key] !== "object") {
|
||||||
schema[key] = { type: schema[key] }
|
schema[key] = { name: key, type: schema[key] }
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -58,19 +102,18 @@ export const deriveStores = context => {
|
||||||
|
|
||||||
const schemaWithRelatedColumns = enrichSchemaWithRelColumns($schema)
|
const schemaWithRelatedColumns = enrichSchemaWithRelColumns($schema)
|
||||||
|
|
||||||
const enrichedSchema = {}
|
const enrichedSchema: Record<string, UIFieldSchema> = {}
|
||||||
Object.keys(schemaWithRelatedColumns).forEach(field => {
|
Object.keys(schemaWithRelatedColumns || {}).forEach(field => {
|
||||||
enrichedSchema[field] = {
|
enrichedSchema[field] = {
|
||||||
...schemaWithRelatedColumns[field],
|
...schemaWithRelatedColumns?.[field],
|
||||||
...$schemaOverrides?.[field],
|
...$schemaOverrides?.[field],
|
||||||
...$schemaMutations[field],
|
...$schemaMutations[field],
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($subSchemaMutations[field]) {
|
if ($subSchemaMutations[field]) {
|
||||||
enrichedSchema[field].columns ??= {}
|
enrichedSchema[field].columns ??= {}
|
||||||
for (const [fieldName, mutation] of Object.entries(
|
for (const fieldName of Object.keys($subSchemaMutations[field])) {
|
||||||
$subSchemaMutations[field]
|
const mutation = $subSchemaMutations[field][fieldName]
|
||||||
)) {
|
|
||||||
enrichedSchema[field].columns[fieldName] = {
|
enrichedSchema[field].columns[fieldName] = {
|
||||||
...enrichedSchema[field].columns[fieldName],
|
...enrichedSchema[field].columns[fieldName],
|
||||||
...mutation,
|
...mutation,
|
||||||
|
@ -87,7 +130,7 @@ export const deriveStores = context => {
|
||||||
([$datasource, $definition]) => {
|
([$datasource, $definition]) => {
|
||||||
let type = $datasource?.type
|
let type = $datasource?.type
|
||||||
if (type === "provider") {
|
if (type === "provider") {
|
||||||
type = $datasource.value?.datasource?.type
|
type = ($datasource as any).value?.datasource?.type
|
||||||
}
|
}
|
||||||
// Handle calculation views
|
// Handle calculation views
|
||||||
if (type === "viewV2" && $definition?.type === ViewV2Type.CALCULATION) {
|
if (type === "viewV2" && $definition?.type === ViewV2Type.CALCULATION) {
|
||||||
|
@ -104,7 +147,7 @@ export const deriveStores = context => {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export const createActions = context => {
|
export const createActions = (context: StoreContext): ActionDatasourceStore => {
|
||||||
const {
|
const {
|
||||||
API,
|
API,
|
||||||
datasource,
|
datasource,
|
||||||
|
@ -147,21 +190,23 @@ export const createActions = context => {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Saves the datasource definition
|
// Saves the datasource definition
|
||||||
const saveDefinition = async newDefinition => {
|
const saveDefinition = async (
|
||||||
|
newDefinition: SaveTableRequest | UpdateViewRequest
|
||||||
|
) => {
|
||||||
// Update local state
|
// Update local state
|
||||||
const originalDefinition = get(definition)
|
const originalDefinition = get(definition)
|
||||||
definition.set(newDefinition)
|
definition.set(newDefinition as UIDatasource)
|
||||||
|
|
||||||
// Update server
|
// Update server
|
||||||
if (get(config).canSaveSchema) {
|
if (get(config).canSaveSchema) {
|
||||||
try {
|
try {
|
||||||
await getAPI()?.actions.saveDefinition(newDefinition)
|
await getAPI()?.actions.saveDefinition(newDefinition as never)
|
||||||
|
|
||||||
// Broadcast change so external state can be updated, as this change
|
// Broadcast change so external state can be updated, as this change
|
||||||
// will not be received by the builder websocket because we caused it
|
// will not be received by the builder websocket because we caused it
|
||||||
// ourselves
|
// ourselves
|
||||||
dispatch("updatedatasource", newDefinition)
|
dispatch("updatedatasource", newDefinition)
|
||||||
} catch (error) {
|
} catch (error: any) {
|
||||||
const msg = error?.message || error || "Unknown error"
|
const msg = error?.message || error || "Unknown error"
|
||||||
get(notifications).error(`Error saving schema: ${msg}`)
|
get(notifications).error(`Error saving schema: ${msg}`)
|
||||||
|
|
||||||
|
@ -172,7 +217,7 @@ export const createActions = context => {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Updates the datasources primary display column
|
// Updates the datasources primary display column
|
||||||
const changePrimaryDisplay = async column => {
|
const changePrimaryDisplay = async (column: string) => {
|
||||||
let newDefinition = cloneDeep(get(definition))
|
let newDefinition = cloneDeep(get(definition))
|
||||||
|
|
||||||
// Update primary display
|
// Update primary display
|
||||||
|
@ -183,12 +228,14 @@ export const createActions = context => {
|
||||||
newDefinition.schema[column].constraints = {}
|
newDefinition.schema[column].constraints = {}
|
||||||
}
|
}
|
||||||
newDefinition.schema[column].constraints.presence = { allowEmpty: false }
|
newDefinition.schema[column].constraints.presence = { allowEmpty: false }
|
||||||
delete newDefinition.schema[column].default
|
if ("default" in newDefinition.schema[column]) {
|
||||||
return await saveDefinition(newDefinition)
|
delete newDefinition.schema[column].default
|
||||||
|
}
|
||||||
|
return await saveDefinition(newDefinition as any)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Adds a schema mutation for a single field
|
// Adds a schema mutation for a single field
|
||||||
const addSchemaMutation = (field, mutation) => {
|
const addSchemaMutation = (field: string, mutation: UIFieldMutation) => {
|
||||||
if (!field || !mutation) {
|
if (!field || !mutation) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -204,7 +251,11 @@ export const createActions = context => {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Adds a nested schema mutation for a single field
|
// Adds a nested schema mutation for a single field
|
||||||
const addSubSchemaMutation = (field, fromField, mutation) => {
|
const addSubSchemaMutation = (
|
||||||
|
field: string,
|
||||||
|
fromField: string,
|
||||||
|
mutation: UIFieldMutation
|
||||||
|
) => {
|
||||||
if (!field || !fromField || !mutation) {
|
if (!field || !fromField || !mutation) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -231,8 +282,8 @@ export const createActions = context => {
|
||||||
const $definition = get(definition)
|
const $definition = get(definition)
|
||||||
const $schemaMutations = get(schemaMutations)
|
const $schemaMutations = get(schemaMutations)
|
||||||
const $subSchemaMutations = get(subSchemaMutations)
|
const $subSchemaMutations = get(subSchemaMutations)
|
||||||
const $schema = get(schema)
|
const $schema = get(schema) || {}
|
||||||
let newSchema = {}
|
let newSchema: Record<string, UIFieldSchema> = {}
|
||||||
|
|
||||||
// Build new updated datasource schema
|
// Build new updated datasource schema
|
||||||
Object.keys($schema).forEach(column => {
|
Object.keys($schema).forEach(column => {
|
||||||
|
@ -242,9 +293,8 @@ export const createActions = context => {
|
||||||
}
|
}
|
||||||
if ($subSchemaMutations[column]) {
|
if ($subSchemaMutations[column]) {
|
||||||
newSchema[column].columns ??= {}
|
newSchema[column].columns ??= {}
|
||||||
for (const [fieldName, mutation] of Object.entries(
|
for (const fieldName of Object.keys($subSchemaMutations[column])) {
|
||||||
$subSchemaMutations[column]
|
const mutation = $subSchemaMutations[column][fieldName]
|
||||||
)) {
|
|
||||||
newSchema[column].columns[fieldName] = {
|
newSchema[column].columns[fieldName] = {
|
||||||
...newSchema[column].columns[fieldName],
|
...newSchema[column].columns[fieldName],
|
||||||
...mutation,
|
...mutation,
|
||||||
|
@ -257,7 +307,7 @@ export const createActions = context => {
|
||||||
await saveDefinition({
|
await saveDefinition({
|
||||||
...$definition,
|
...$definition,
|
||||||
schema: newSchema,
|
schema: newSchema,
|
||||||
})
|
} as any)
|
||||||
resetSchemaMutations()
|
resetSchemaMutations()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -267,32 +317,32 @@ export const createActions = context => {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Adds a row to the datasource
|
// Adds a row to the datasource
|
||||||
const addRow = async row => {
|
const addRow = async (row: SaveRowRequest) => {
|
||||||
return await getAPI()?.actions.addRow(row)
|
return await getAPI()?.actions.addRow(row)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Updates an existing row in the datasource
|
// Updates an existing row in the datasource
|
||||||
const updateRow = async row => {
|
const updateRow = async (row: SaveRowRequest) => {
|
||||||
return await getAPI()?.actions.updateRow(row)
|
return await getAPI()?.actions.updateRow(row)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Deletes rows from the datasource
|
// Deletes rows from the datasource
|
||||||
const deleteRows = async rows => {
|
const deleteRows = async (rows: Row[]) => {
|
||||||
return await getAPI()?.actions.deleteRows(rows)
|
return await getAPI()?.actions.deleteRows(rows)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Gets a single row from a datasource
|
// Gets a single row from a datasource
|
||||||
const getRow = async id => {
|
const getRow = async (id: string) => {
|
||||||
return await getAPI()?.actions.getRow(id)
|
return await getAPI()?.actions.getRow(id)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Checks if a certain datasource config is valid
|
// Checks if a certain datasource config is valid
|
||||||
const isDatasourceValid = datasource => {
|
const isDatasourceValid = (datasource: UIDatasource) => {
|
||||||
return getAPI()?.actions.isDatasourceValid(datasource)
|
return getAPI()?.actions.isDatasourceValid(datasource)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Checks if this datasource can use a specific column by name
|
// Checks if this datasource can use a specific column by name
|
||||||
const canUseColumn = name => {
|
const canUseColumn = (name: string) => {
|
||||||
return getAPI()?.actions.canUseColumn(name)
|
return getAPI()?.actions.canUseColumn(name)
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,31 @@
|
||||||
|
import {
|
||||||
|
Row,
|
||||||
|
SaveRowRequest,
|
||||||
|
SaveTableRequest,
|
||||||
|
UIDatasource,
|
||||||
|
UpdateViewRequest,
|
||||||
|
} from "@budibase/types"
|
||||||
|
|
||||||
|
interface DatasourceBaseActions<
|
||||||
|
TSaveDefinitionRequest = UpdateViewRequest | SaveTableRequest
|
||||||
|
> {
|
||||||
|
saveDefinition: (newDefinition: TSaveDefinitionRequest) => Promise<void>
|
||||||
|
addRow: (row: SaveRowRequest) => Promise<Row | void>
|
||||||
|
updateRow: (row: SaveRowRequest) => Promise<Row | void>
|
||||||
|
deleteRows: (rows: Row[]) => Promise<void>
|
||||||
|
getRow: (id: string) => Promise<Row | void>
|
||||||
|
isDatasourceValid: (datasource: UIDatasource) => boolean | void
|
||||||
|
canUseColumn: (name: string) => boolean | void
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface DatasourceTableActions
|
||||||
|
extends DatasourceBaseActions<SaveTableRequest> {}
|
||||||
|
|
||||||
|
export interface DatasourceViewActions
|
||||||
|
extends DatasourceBaseActions<UpdateViewRequest> {}
|
||||||
|
|
||||||
|
export interface DatasourceNonPlusActions
|
||||||
|
extends DatasourceBaseActions<never> {}
|
||||||
|
|
||||||
|
export type DatasourceActions =
|
||||||
|
| DatasourceTableActions & DatasourceViewActions & DatasourceNonPlusActions
|
|
@ -1,18 +1,11 @@
|
||||||
import { SortOrder, UIDatasource } from "@budibase/types"
|
import { SortOrder, UIDatasource } from "@budibase/types"
|
||||||
import { get } from "svelte/store"
|
import { get } from "svelte/store"
|
||||||
import { Store as StoreContext } from ".."
|
import { Store as StoreContext } from ".."
|
||||||
|
import { DatasourceNonPlusActions } from "."
|
||||||
|
|
||||||
interface NonPlusActions {
|
interface NonPlusActions {
|
||||||
nonPlus: {
|
nonPlus: {
|
||||||
actions: {
|
actions: DatasourceNonPlusActions
|
||||||
saveDefinition: () => Promise<void>
|
|
||||||
addRow: () => Promise<void>
|
|
||||||
updateRow: () => Promise<void>
|
|
||||||
deleteRows: () => Promise<void>
|
|
||||||
getRow: () => Promise<void>
|
|
||||||
isDatasourceValid: (datasource: UIDatasource) => boolean
|
|
||||||
canUseColumn: (name: string) => boolean
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,27 +1,19 @@
|
||||||
import {
|
import {
|
||||||
Row,
|
Row,
|
||||||
SaveRowRequest,
|
SaveRowRequest,
|
||||||
SaveRowResponse,
|
|
||||||
SaveTableRequest,
|
SaveTableRequest,
|
||||||
SortOrder,
|
SortOrder,
|
||||||
UIDatasource,
|
UIDatasource,
|
||||||
} from "@budibase/types"
|
} from "@budibase/types"
|
||||||
import { get } from "svelte/store"
|
import { get } from "svelte/store"
|
||||||
import { Store as StoreContext } from ".."
|
import { Store as StoreContext } from ".."
|
||||||
|
import { DatasourceTableActions } from "."
|
||||||
|
|
||||||
const SuppressErrors = true
|
const SuppressErrors = true
|
||||||
|
|
||||||
interface TableActions {
|
interface TableActions {
|
||||||
table: {
|
table: {
|
||||||
actions: {
|
actions: DatasourceTableActions
|
||||||
saveDefinition: (newDefinition: SaveTableRequest) => Promise<void>
|
|
||||||
addRow: (row: SaveRowRequest) => Promise<SaveRowResponse>
|
|
||||||
updateRow: (row: SaveRowRequest) => Promise<SaveRowResponse>
|
|
||||||
deleteRows: (rows: (string | Row)[]) => Promise<void>
|
|
||||||
getRow: (id: string) => Promise<Row>
|
|
||||||
isDatasourceValid: (datasource: UIDatasource) => boolean
|
|
||||||
canUseColumn: (name: string) => boolean
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -42,7 +34,7 @@ export const createActions = (context: StoreContext): TableActions => {
|
||||||
return await API.saveRow(row, SuppressErrors)
|
return await API.saveRow(row, SuppressErrors)
|
||||||
}
|
}
|
||||||
|
|
||||||
const deleteRows = async (rows: (string | Row)[]) => {
|
const deleteRows = async (rows: Row[]) => {
|
||||||
await API.deleteRows(get(datasource).tableId, rows)
|
await API.deleteRows(get(datasource).tableId, rows)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -4,23 +4,17 @@ import {
|
||||||
SaveRowRequest,
|
SaveRowRequest,
|
||||||
SortOrder,
|
SortOrder,
|
||||||
UIDatasource,
|
UIDatasource,
|
||||||
|
UIView,
|
||||||
UpdateViewRequest,
|
UpdateViewRequest,
|
||||||
} from "@budibase/types"
|
} from "@budibase/types"
|
||||||
import { Store as StoreContext } from ".."
|
import { Store as StoreContext } from ".."
|
||||||
|
import { DatasourceViewActions } from "."
|
||||||
|
|
||||||
const SuppressErrors = true
|
const SuppressErrors = true
|
||||||
|
|
||||||
interface ViewActions {
|
interface ViewActions {
|
||||||
viewV2: {
|
viewV2: {
|
||||||
actions: {
|
actions: DatasourceViewActions
|
||||||
saveDefinition: (newDefinition: UpdateViewRequest) => Promise<void>
|
|
||||||
addRow: (row: SaveRowRequest) => Promise<Row>
|
|
||||||
updateRow: (row: SaveRowRequest) => Promise<Row>
|
|
||||||
deleteRows: (rows: (string | Row)[]) => Promise<void>
|
|
||||||
getRow: (id: string) => Promise<Row>
|
|
||||||
isDatasourceValid: (datasource: UIDatasource) => boolean
|
|
||||||
canUseColumn: (name: string) => boolean
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -46,7 +40,7 @@ export const createActions = (context: StoreContext): ViewActions => {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const deleteRows = async (rows: (string | Row)[]) => {
|
const deleteRows = async (rows: Row[]) => {
|
||||||
await API.deleteRows(get(datasource).id, rows)
|
await API.deleteRows(get(datasource).id, rows)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -154,7 +148,7 @@ export const initialise = (context: StoreContext) => {
|
||||||
unsubscribers.push(
|
unsubscribers.push(
|
||||||
sort.subscribe(async $sort => {
|
sort.subscribe(async $sort => {
|
||||||
// Ensure we're updating the correct view
|
// Ensure we're updating the correct view
|
||||||
const $view = get(definition)
|
const $view = get(definition) as UIView
|
||||||
if ($view?.id !== $datasource.id) {
|
if ($view?.id !== $datasource.id) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -205,7 +199,7 @@ export const initialise = (context: StoreContext) => {
|
||||||
await datasource.actions.saveDefinition({
|
await datasource.actions.saveDefinition({
|
||||||
...$view,
|
...$view,
|
||||||
queryUI: $filter,
|
queryUI: $filter,
|
||||||
})
|
} as never as UpdateViewRequest)
|
||||||
|
|
||||||
// Refresh data since view definition changed
|
// Refresh data since view definition changed
|
||||||
await rows.actions.refreshData()
|
await rows.actions.refreshData()
|
||||||
|
|
|
@ -59,11 +59,9 @@ export type Store = BaseStore &
|
||||||
Columns.Store &
|
Columns.Store &
|
||||||
Table.Store &
|
Table.Store &
|
||||||
ViewV2.Store &
|
ViewV2.Store &
|
||||||
NonPlus.Store & {
|
NonPlus.Store &
|
||||||
|
Datasource.Store & {
|
||||||
// TODO while typing the rest of stores
|
// TODO while typing the rest of stores
|
||||||
datasource: Writable<any> & { actions: any }
|
|
||||||
definition: Writable<any>
|
|
||||||
enrichedSchema: any
|
|
||||||
fetch: Writable<any>
|
fetch: Writable<any>
|
||||||
filter: Writable<any>
|
filter: Writable<any>
|
||||||
inlineFilters: Writable<any>
|
inlineFilters: Writable<any>
|
||||||
|
@ -75,6 +73,9 @@ export type Store = BaseStore &
|
||||||
rows: Writable<any> & { actions: any }
|
rows: Writable<any> & { actions: any }
|
||||||
subscribe: any
|
subscribe: any
|
||||||
config: Writable<any>
|
config: Writable<any>
|
||||||
|
dispatch: (event: string, data: any) => any
|
||||||
|
notifications: Writable<any>
|
||||||
|
schemaOverrides: Writable<any>
|
||||||
}
|
}
|
||||||
|
|
||||||
export const attachStores = (context: Store): Store => {
|
export const attachStores = (context: Store): Store => {
|
||||||
|
@ -106,5 +107,5 @@ export const attachStores = (context: Store): Store => {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return context
|
return context as Store
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,103 +0,0 @@
|
||||||
import { FieldType, RelationshipType } from "@budibase/types"
|
|
||||||
import { Helpers } from "@budibase/bbui"
|
|
||||||
|
|
||||||
const columnTypeManyTypeOverrides = {
|
|
||||||
[FieldType.DATETIME]: FieldType.STRING,
|
|
||||||
[FieldType.BOOLEAN]: FieldType.STRING,
|
|
||||||
[FieldType.SIGNATURE_SINGLE]: FieldType.ATTACHMENTS,
|
|
||||||
}
|
|
||||||
|
|
||||||
const columnTypeManyParser = {
|
|
||||||
[FieldType.DATETIME]: (value, field) => {
|
|
||||||
function parseDate(value) {
|
|
||||||
const { timeOnly, dateOnly, ignoreTimezones } = field || {}
|
|
||||||
const enableTime = !dateOnly
|
|
||||||
const parsedValue = Helpers.parseDate(value, {
|
|
||||||
timeOnly,
|
|
||||||
enableTime,
|
|
||||||
ignoreTimezones,
|
|
||||||
})
|
|
||||||
const parsed = Helpers.getDateDisplayValue(parsedValue, {
|
|
||||||
enableTime,
|
|
||||||
timeOnly,
|
|
||||||
})
|
|
||||||
return parsed
|
|
||||||
}
|
|
||||||
|
|
||||||
return value.map(v => parseDate(v))
|
|
||||||
},
|
|
||||||
[FieldType.BOOLEAN]: value => value.map(v => !!v),
|
|
||||||
[FieldType.BB_REFERENCE_SINGLE]: value => [
|
|
||||||
...new Map(value.map(i => [i._id, i])).values(),
|
|
||||||
],
|
|
||||||
[FieldType.BB_REFERENCE]: value => [
|
|
||||||
...new Map(value.map(i => [i._id, i])).values(),
|
|
||||||
],
|
|
||||||
[FieldType.ARRAY]: value => Array.from(new Set(value)),
|
|
||||||
}
|
|
||||||
|
|
||||||
export function enrichSchemaWithRelColumns(schema) {
|
|
||||||
if (!schema) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
const result = Object.keys(schema).reduce((result, fieldName) => {
|
|
||||||
const field = schema[fieldName]
|
|
||||||
result[fieldName] = field
|
|
||||||
|
|
||||||
if (field.visible !== false && field.columns) {
|
|
||||||
const fromSingle =
|
|
||||||
field?.relationshipType === RelationshipType.ONE_TO_MANY
|
|
||||||
|
|
||||||
for (const relColumn of Object.keys(field.columns)) {
|
|
||||||
const relField = field.columns[relColumn]
|
|
||||||
if (!relField.visible) {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
const name = `${field.name}.${relColumn}`
|
|
||||||
result[name] = {
|
|
||||||
...relField,
|
|
||||||
name,
|
|
||||||
related: { field: fieldName, subField: relColumn },
|
|
||||||
cellRenderType:
|
|
||||||
(!fromSingle && columnTypeManyTypeOverrides[relField.type]) ||
|
|
||||||
relField.type,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return result
|
|
||||||
}, {})
|
|
||||||
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
export function getRelatedTableValues(row, field, fromField) {
|
|
||||||
const fromSingle =
|
|
||||||
fromField?.relationshipType === RelationshipType.ONE_TO_MANY
|
|
||||||
|
|
||||||
let result = ""
|
|
||||||
|
|
||||||
if (fromSingle) {
|
|
||||||
result = row[field.related.field]?.[0]?.[field.related.subField]
|
|
||||||
} else {
|
|
||||||
const parser = columnTypeManyParser[field.type] || (value => value)
|
|
||||||
const value = row[field.related.field]
|
|
||||||
?.flatMap(r => r[field.related.subField])
|
|
||||||
?.filter(i => i !== undefined && i !== null)
|
|
||||||
result = parser(value || [], field)
|
|
||||||
if (
|
|
||||||
[
|
|
||||||
FieldType.STRING,
|
|
||||||
FieldType.NUMBER,
|
|
||||||
FieldType.BIGINT,
|
|
||||||
FieldType.BOOLEAN,
|
|
||||||
FieldType.DATETIME,
|
|
||||||
FieldType.LONGFORM,
|
|
||||||
FieldType.BARCODEQR,
|
|
||||||
].includes(field.type)
|
|
||||||
) {
|
|
||||||
result = result?.join(", ")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return result
|
|
||||||
}
|
|
|
@ -0,0 +1,129 @@
|
||||||
|
import { Helpers } from "@budibase/bbui"
|
||||||
|
import {
|
||||||
|
FieldType,
|
||||||
|
isRelationshipField,
|
||||||
|
RelationshipType,
|
||||||
|
Row,
|
||||||
|
UIFieldSchema,
|
||||||
|
} from "@budibase/types"
|
||||||
|
|
||||||
|
const columnTypeManyTypeOverrides: Partial<Record<FieldType, FieldType>> = {
|
||||||
|
[FieldType.DATETIME]: FieldType.STRING,
|
||||||
|
[FieldType.BOOLEAN]: FieldType.STRING,
|
||||||
|
[FieldType.SIGNATURE_SINGLE]: FieldType.ATTACHMENTS,
|
||||||
|
}
|
||||||
|
|
||||||
|
const columnTypeManyParser = {
|
||||||
|
[FieldType.DATETIME]: (
|
||||||
|
value: any[],
|
||||||
|
field: {
|
||||||
|
timeOnly?: boolean
|
||||||
|
dateOnly?: boolean
|
||||||
|
}
|
||||||
|
) => {
|
||||||
|
function parseDate(value: any) {
|
||||||
|
const { timeOnly, dateOnly } = field || {}
|
||||||
|
const enableTime = !dateOnly
|
||||||
|
const parsedValue = Helpers.parseDate(value, { enableTime })
|
||||||
|
const parsed = Helpers.getDateDisplayValue(parsedValue, {
|
||||||
|
enableTime,
|
||||||
|
timeOnly,
|
||||||
|
})
|
||||||
|
return parsed
|
||||||
|
}
|
||||||
|
|
||||||
|
return value.map(v => parseDate(v))
|
||||||
|
},
|
||||||
|
[FieldType.BOOLEAN]: (value: any[]) => value.map(v => !!v),
|
||||||
|
[FieldType.BB_REFERENCE_SINGLE]: (value: any[]) => [
|
||||||
|
...new Map(value.map(i => [i._id, i])).values(),
|
||||||
|
],
|
||||||
|
[FieldType.BB_REFERENCE]: (value: any[]) => [
|
||||||
|
...new Map(value.map(i => [i._id, i])).values(),
|
||||||
|
],
|
||||||
|
[FieldType.ARRAY]: (value: any[]) => Array.from(new Set(value)),
|
||||||
|
}
|
||||||
|
|
||||||
|
export function enrichSchemaWithRelColumns(
|
||||||
|
schema: Record<string, UIFieldSchema>
|
||||||
|
): Record<string, UIFieldSchema> | undefined {
|
||||||
|
if (!schema) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
const result = Object.keys(schema).reduce<Record<string, UIFieldSchema>>(
|
||||||
|
(result, fieldName) => {
|
||||||
|
const field = schema[fieldName]
|
||||||
|
result[fieldName] = field
|
||||||
|
|
||||||
|
if (
|
||||||
|
field.visible !== false &&
|
||||||
|
isRelationshipField(field) &&
|
||||||
|
field.columns
|
||||||
|
) {
|
||||||
|
const fromSingle =
|
||||||
|
field?.relationshipType === RelationshipType.ONE_TO_MANY
|
||||||
|
|
||||||
|
for (const relColumn of Object.keys(field.columns)) {
|
||||||
|
const relField = field.columns[relColumn]
|
||||||
|
if (!relField.visible) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
const name = `${field.name}.${relColumn}`
|
||||||
|
result[name] = {
|
||||||
|
...relField,
|
||||||
|
type: relField.type as any, // TODO
|
||||||
|
name,
|
||||||
|
related: { field: fieldName, subField: relColumn },
|
||||||
|
cellRenderType:
|
||||||
|
(!fromSingle && columnTypeManyTypeOverrides[relField.type]) ||
|
||||||
|
relField.type,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
},
|
||||||
|
{}
|
||||||
|
)
|
||||||
|
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getRelatedTableValues(
|
||||||
|
row: Row,
|
||||||
|
field: UIFieldSchema & { related: { field: string; subField: string } },
|
||||||
|
fromField: UIFieldSchema
|
||||||
|
) {
|
||||||
|
const fromSingle =
|
||||||
|
isRelationshipField(fromField) &&
|
||||||
|
fromField?.relationshipType === RelationshipType.ONE_TO_MANY
|
||||||
|
|
||||||
|
let result = ""
|
||||||
|
|
||||||
|
if (fromSingle) {
|
||||||
|
result = row[field.related.field]?.[0]?.[field.related.subField]
|
||||||
|
} else {
|
||||||
|
const parser =
|
||||||
|
columnTypeManyParser[field.type as keyof typeof columnTypeManyParser] ||
|
||||||
|
((value: any) => value)
|
||||||
|
const value = row[field.related.field]
|
||||||
|
?.flatMap((r: Row) => r[field.related.subField])
|
||||||
|
?.filter((i: any) => i !== undefined && i !== null)
|
||||||
|
const parsed = parser(value || [], field as any)
|
||||||
|
result = parsed as any
|
||||||
|
if (
|
||||||
|
[
|
||||||
|
FieldType.STRING,
|
||||||
|
FieldType.NUMBER,
|
||||||
|
FieldType.BIGINT,
|
||||||
|
FieldType.BOOLEAN,
|
||||||
|
FieldType.DATETIME,
|
||||||
|
FieldType.LONGFORM,
|
||||||
|
FieldType.BARCODEQR,
|
||||||
|
].includes(field.type)
|
||||||
|
) {
|
||||||
|
result = parsed?.join(", ")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result
|
||||||
|
}
|
|
@ -1,5 +1,11 @@
|
||||||
export interface UIDatasource {
|
import { UITable, UIView } from "@budibase/types"
|
||||||
|
|
||||||
|
export type UIDatasource = (UITable | UIView) & {
|
||||||
type: string
|
type: string
|
||||||
id: string
|
}
|
||||||
tableId: string
|
|
||||||
|
export interface UIFieldMutation {
|
||||||
|
visible?: boolean
|
||||||
|
readonly?: boolean
|
||||||
|
width?: number
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,2 +1,4 @@
|
||||||
export * from "./columns"
|
export * from "./columns"
|
||||||
export * from "./datasource"
|
export * from "./datasource"
|
||||||
|
export * from "./table"
|
||||||
|
export * from "./view"
|
||||||
|
|
|
@ -0,0 +1,34 @@
|
||||||
|
import {
|
||||||
|
BasicViewFieldMetadata,
|
||||||
|
FieldSchema,
|
||||||
|
FieldType,
|
||||||
|
RelationSchemaField,
|
||||||
|
SortOrder,
|
||||||
|
Table,
|
||||||
|
UISearchFilter,
|
||||||
|
} from "@budibase/types"
|
||||||
|
|
||||||
|
export interface UITable extends Omit<Table, "type"> {
|
||||||
|
name: string
|
||||||
|
id: string
|
||||||
|
type: string
|
||||||
|
tableId: string
|
||||||
|
primaryDisplay?: string
|
||||||
|
sort?: {
|
||||||
|
field: string
|
||||||
|
order: SortOrder
|
||||||
|
}
|
||||||
|
queryUI: UISearchFilter
|
||||||
|
schema: Record<string, UIFieldSchema>
|
||||||
|
}
|
||||||
|
|
||||||
|
export type UIFieldSchema = FieldSchema &
|
||||||
|
BasicViewFieldMetadata & {
|
||||||
|
related?: { field: string; subField: string }
|
||||||
|
columns?: Record<string, UIRelationSchemaField>
|
||||||
|
cellRenderType?: string
|
||||||
|
}
|
||||||
|
|
||||||
|
interface UIRelationSchemaField extends RelationSchemaField {
|
||||||
|
type: FieldType
|
||||||
|
}
|
|
@ -0,0 +1,6 @@
|
||||||
|
import { ViewV2 } from "@budibase/types"
|
||||||
|
import { UIFieldSchema } from "./table"
|
||||||
|
|
||||||
|
export interface UIView extends ViewV2 {
|
||||||
|
schema: Record<string, UIFieldSchema>
|
||||||
|
}
|
Loading…
Reference in New Issue