diff --git a/packages/bbui/src/Form/Core/DatePicker/NumberInput.svelte b/packages/bbui/src/Form/Core/DatePicker/NumberInput.svelte index 6c06ce4e79..84b55c403f 100644 --- a/packages/bbui/src/Form/Core/DatePicker/NumberInput.svelte +++ b/packages/bbui/src/Form/Core/DatePicker/NumberInput.svelte @@ -51,6 +51,7 @@ } input.hide-arrows { -moz-appearance: textfield; + appearance: textfield; } input[type="time"]::-webkit-calendar-picker-indicator { display: none; diff --git a/packages/bbui/src/Form/Core/Slider.svelte b/packages/bbui/src/Form/Core/Slider.svelte index 1a601d0185..0fcadd8bad 100644 --- a/packages/bbui/src/Form/Core/Slider.svelte +++ b/packages/bbui/src/Form/Core/Slider.svelte @@ -39,6 +39,7 @@ padding: 0; margin: 0; -webkit-appearance: none; + appearance: none; background: transparent; } input::-webkit-slider-thumb { diff --git a/packages/bbui/src/Tabs/Tabs.svelte b/packages/bbui/src/Tabs/Tabs.svelte index c94b396398..3955145ad1 100644 --- a/packages/bbui/src/Tabs/Tabs.svelte +++ b/packages/bbui/src/Tabs/Tabs.svelte @@ -124,8 +124,6 @@ .spectrum-Tabs-selectionIndicator.emphasized { background-color: var(--spectrum-global-color-blue-400); } - .spectrum-Tabs--horizontal .spectrum-Tabs-selectionIndicator { - } .noHorizPadding { padding: 0; } diff --git a/packages/bbui/src/Tooltip/AbsTooltip.svelte b/packages/bbui/src/Tooltip/AbsTooltip.svelte index 4d2399aaf8..b85f4e1c03 100644 --- a/packages/bbui/src/Tooltip/AbsTooltip.svelte +++ b/packages/bbui/src/Tooltip/AbsTooltip.svelte @@ -134,6 +134,7 @@ .spectrum-Tooltip-label { display: -webkit-box; -webkit-line-clamp: 3; + line-clamp: 3; -webkit-box-orient: vertical; overflow: hidden; font-size: 12px; diff --git a/packages/frontend-core/src/components/grid/cells/AICell.svelte b/packages/frontend-core/src/components/grid/cells/AICell.svelte index 38e81cefd3..b56e67b752 100644 --- a/packages/frontend-core/src/components/grid/cells/AICell.svelte +++ b/packages/frontend-core/src/components/grid/cells/AICell.svelte @@ -73,6 +73,7 @@ .value { display: -webkit-box; -webkit-line-clamp: var(--content-lines); + line-clamp: var(--content-lines); -webkit-box-orient: vertical; overflow: hidden; line-height: 20px; diff --git a/packages/frontend-core/src/components/grid/cells/LongFormCell.svelte b/packages/frontend-core/src/components/grid/cells/LongFormCell.svelte index 7829e5da7d..80da91e091 100644 --- a/packages/frontend-core/src/components/grid/cells/LongFormCell.svelte +++ b/packages/frontend-core/src/components/grid/cells/LongFormCell.svelte @@ -93,6 +93,7 @@ .value { display: -webkit-box; -webkit-line-clamp: var(--content-lines); + line-clamp: var(--content-lines); -webkit-box-orient: vertical; overflow: hidden; line-height: 20px; diff --git a/packages/frontend-core/src/components/grid/cells/TextCell.svelte b/packages/frontend-core/src/components/grid/cells/TextCell.svelte index 9275bca3c6..b9a63eb401 100644 --- a/packages/frontend-core/src/components/grid/cells/TextCell.svelte +++ b/packages/frontend-core/src/components/grid/cells/TextCell.svelte @@ -74,12 +74,14 @@ .value { display: -webkit-box; -webkit-line-clamp: var(--content-lines); + line-clamp: var(--content-lines); -webkit-box-orient: vertical; overflow: hidden; line-height: 20px; } .number .value { -webkit-line-clamp: 1; + line-clamp: 1; } input { flex: 1 1 auto; @@ -110,5 +112,6 @@ } input[type="number"] { -moz-appearance: textfield; + appearance: textfield; } diff --git a/packages/frontend-core/src/components/grid/stores/datasource.js b/packages/frontend-core/src/components/grid/stores/datasource.ts similarity index 67% rename from packages/frontend-core/src/components/grid/stores/datasource.js rename to packages/frontend-core/src/components/grid/stores/datasource.ts index 6aa607f7ed..7aee6e8515 100644 --- a/packages/frontend-core/src/components/grid/stores/datasource.js +++ b/packages/frontend-core/src/components/grid/stores/datasource.ts @@ -1,10 +1,54 @@ -import { derived, get } from "svelte/store" +import { derived, get, Readable, Writable } from "svelte/store" import { getDatasourceDefinition, getDatasourceSchema } from "../../../fetch" import { enrichSchemaWithRelColumns, memo } from "../../../utils" 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 + schemaMutations: Writable> + subSchemaMutations: Writable>> +} + +interface DerivedDatasourceStore { + schema: Readable | null> + enrichedSchema: Readable | null> + hasBudibaseIdentifiers: Readable +} + +interface ActionDatasourceStore { + datasource: DatasourceStore["definition"] & { + actions: DatasourceActions & { + refreshDefinition: () => Promise + changePrimaryDisplay: (column: string) => Promise + addSchemaMutation: (field: string, mutation: UIFieldMutation) => void + addSubSchemaMutation: ( + field: string, + fromField: string, + mutation: UIFieldMutation + ) => void + saveSchemaMutations: () => Promise + resetSchemaMutations: () => void + } + } +} + +export type Store = DatasourceStore & + DerivedDatasourceStore & + ActionDatasourceStore + +export const createStores = (): DatasourceStore => { const definition = memo(null) const schemaMutations = memo({}) const subSchemaMutations = memo({}) @@ -16,7 +60,7 @@ export const createStores = () => { } } -export const deriveStores = context => { +export const deriveStores = (context: StoreContext): DerivedDatasourceStore => { const { API, definition, @@ -27,7 +71,7 @@ export const deriveStores = context => { } = context const schema = derived(definition, $definition => { - let schema = getDatasourceSchema({ + let schema: Record = getDatasourceSchema({ API, datasource: get(datasource), definition: $definition, @@ -40,7 +84,7 @@ export const deriveStores = context => { // Certain datasources like queries use primitives. Object.keys(schema || {}).forEach(key => { 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 enrichedSchema = {} - Object.keys(schemaWithRelatedColumns).forEach(field => { + const enrichedSchema: Record = {} + Object.keys(schemaWithRelatedColumns || {}).forEach(field => { enrichedSchema[field] = { - ...schemaWithRelatedColumns[field], + ...schemaWithRelatedColumns?.[field], ...$schemaOverrides?.[field], ...$schemaMutations[field], } if ($subSchemaMutations[field]) { enrichedSchema[field].columns ??= {} - for (const [fieldName, mutation] of Object.entries( - $subSchemaMutations[field] - )) { + for (const fieldName of Object.keys($subSchemaMutations[field])) { + const mutation = $subSchemaMutations[field][fieldName] enrichedSchema[field].columns[fieldName] = { ...enrichedSchema[field].columns[fieldName], ...mutation, @@ -87,7 +130,7 @@ export const deriveStores = context => { ([$datasource, $definition]) => { let type = $datasource?.type if (type === "provider") { - type = $datasource.value?.datasource?.type + type = ($datasource as any).value?.datasource?.type } // Handle calculation views 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 { API, datasource, @@ -147,21 +190,23 @@ export const createActions = context => { } // Saves the datasource definition - const saveDefinition = async newDefinition => { + const saveDefinition = async ( + newDefinition: SaveTableRequest | UpdateViewRequest + ) => { // Update local state const originalDefinition = get(definition) - definition.set(newDefinition) + definition.set(newDefinition as UIDatasource) // Update server if (get(config).canSaveSchema) { try { - await getAPI()?.actions.saveDefinition(newDefinition) + await getAPI()?.actions.saveDefinition(newDefinition as never) // Broadcast change so external state can be updated, as this change // will not be received by the builder websocket because we caused it // ourselves dispatch("updatedatasource", newDefinition) - } catch (error) { + } catch (error: any) { const msg = error?.message || error || "Unknown error" get(notifications).error(`Error saving schema: ${msg}`) @@ -172,7 +217,7 @@ export const createActions = context => { } // Updates the datasources primary display column - const changePrimaryDisplay = async column => { + const changePrimaryDisplay = async (column: string) => { let newDefinition = cloneDeep(get(definition)) // Update primary display @@ -183,12 +228,14 @@ export const createActions = context => { newDefinition.schema[column].constraints = {} } newDefinition.schema[column].constraints.presence = { allowEmpty: false } - delete newDefinition.schema[column].default - return await saveDefinition(newDefinition) + if ("default" in newDefinition.schema[column]) { + delete newDefinition.schema[column].default + } + return await saveDefinition(newDefinition as any) } // Adds a schema mutation for a single field - const addSchemaMutation = (field, mutation) => { + const addSchemaMutation = (field: string, mutation: UIFieldMutation) => { if (!field || !mutation) { return } @@ -204,7 +251,11 @@ export const createActions = context => { } // 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) { return } @@ -231,8 +282,8 @@ export const createActions = context => { const $definition = get(definition) const $schemaMutations = get(schemaMutations) const $subSchemaMutations = get(subSchemaMutations) - const $schema = get(schema) - let newSchema = {} + const $schema = get(schema) || {} + let newSchema: Record = {} // Build new updated datasource schema Object.keys($schema).forEach(column => { @@ -242,9 +293,8 @@ export const createActions = context => { } if ($subSchemaMutations[column]) { newSchema[column].columns ??= {} - for (const [fieldName, mutation] of Object.entries( - $subSchemaMutations[column] - )) { + for (const fieldName of Object.keys($subSchemaMutations[column])) { + const mutation = $subSchemaMutations[column][fieldName] newSchema[column].columns[fieldName] = { ...newSchema[column].columns[fieldName], ...mutation, @@ -257,7 +307,7 @@ export const createActions = context => { await saveDefinition({ ...$definition, schema: newSchema, - }) + } as any) resetSchemaMutations() } @@ -267,32 +317,32 @@ export const createActions = context => { } // Adds a row to the datasource - const addRow = async row => { + const addRow = async (row: SaveRowRequest) => { return await getAPI()?.actions.addRow(row) } // Updates an existing row in the datasource - const updateRow = async row => { + const updateRow = async (row: SaveRowRequest) => { return await getAPI()?.actions.updateRow(row) } // Deletes rows from the datasource - const deleteRows = async rows => { + const deleteRows = async (rows: Row[]) => { return await getAPI()?.actions.deleteRows(rows) } // Gets a single row from a datasource - const getRow = async id => { + const getRow = async (id: string) => { return await getAPI()?.actions.getRow(id) } // Checks if a certain datasource config is valid - const isDatasourceValid = datasource => { + const isDatasourceValid = (datasource: UIDatasource) => { return getAPI()?.actions.isDatasourceValid(datasource) } // Checks if this datasource can use a specific column by name - const canUseColumn = name => { + const canUseColumn = (name: string) => { return getAPI()?.actions.canUseColumn(name) } diff --git a/packages/frontend-core/src/components/grid/stores/datasources/index.ts b/packages/frontend-core/src/components/grid/stores/datasources/index.ts new file mode 100644 index 0000000000..c58aef37e9 --- /dev/null +++ b/packages/frontend-core/src/components/grid/stores/datasources/index.ts @@ -0,0 +1,31 @@ +import { + Row, + SaveRowRequest, + SaveTableRequest, + UIDatasource, + UpdateViewRequest, +} from "@budibase/types" + +interface DatasourceBaseActions< + TSaveDefinitionRequest = UpdateViewRequest | SaveTableRequest +> { + saveDefinition: (newDefinition: TSaveDefinitionRequest) => Promise + addRow: (row: SaveRowRequest) => Promise + updateRow: (row: SaveRowRequest) => Promise + deleteRows: (rows: Row[]) => Promise + getRow: (id: string) => Promise + isDatasourceValid: (datasource: UIDatasource) => boolean | void + canUseColumn: (name: string) => boolean | void +} + +export interface DatasourceTableActions + extends DatasourceBaseActions {} + +export interface DatasourceViewActions + extends DatasourceBaseActions {} + +export interface DatasourceNonPlusActions + extends DatasourceBaseActions {} + +export type DatasourceActions = + | DatasourceTableActions & DatasourceViewActions & DatasourceNonPlusActions diff --git a/packages/frontend-core/src/components/grid/stores/datasources/nonPlus.ts b/packages/frontend-core/src/components/grid/stores/datasources/nonPlus.ts index dcc4d47076..17e5e8b8d9 100644 --- a/packages/frontend-core/src/components/grid/stores/datasources/nonPlus.ts +++ b/packages/frontend-core/src/components/grid/stores/datasources/nonPlus.ts @@ -1,18 +1,11 @@ import { SortOrder, UIDatasource } from "@budibase/types" import { get } from "svelte/store" import { Store as StoreContext } from ".." +import { DatasourceNonPlusActions } from "." interface NonPlusActions { nonPlus: { - actions: { - saveDefinition: () => Promise - addRow: () => Promise - updateRow: () => Promise - deleteRows: () => Promise - getRow: () => Promise - isDatasourceValid: (datasource: UIDatasource) => boolean - canUseColumn: (name: string) => boolean - } + actions: DatasourceNonPlusActions } } diff --git a/packages/frontend-core/src/components/grid/stores/datasources/table.ts b/packages/frontend-core/src/components/grid/stores/datasources/table.ts index e905c89e44..894a65ba4c 100644 --- a/packages/frontend-core/src/components/grid/stores/datasources/table.ts +++ b/packages/frontend-core/src/components/grid/stores/datasources/table.ts @@ -1,27 +1,19 @@ import { Row, SaveRowRequest, - SaveRowResponse, SaveTableRequest, SortOrder, UIDatasource, } from "@budibase/types" import { get } from "svelte/store" import { Store as StoreContext } from ".." +import { DatasourceTableActions } from "." const SuppressErrors = true interface TableActions { table: { - actions: { - saveDefinition: (newDefinition: SaveTableRequest) => Promise - addRow: (row: SaveRowRequest) => Promise - updateRow: (row: SaveRowRequest) => Promise - deleteRows: (rows: (string | Row)[]) => Promise - getRow: (id: string) => Promise - isDatasourceValid: (datasource: UIDatasource) => boolean - canUseColumn: (name: string) => boolean - } + actions: DatasourceTableActions } } @@ -42,7 +34,7 @@ export const createActions = (context: StoreContext): TableActions => { return await API.saveRow(row, SuppressErrors) } - const deleteRows = async (rows: (string | Row)[]) => { + const deleteRows = async (rows: Row[]) => { await API.deleteRows(get(datasource).tableId, rows) } diff --git a/packages/frontend-core/src/components/grid/stores/datasources/viewV2.ts b/packages/frontend-core/src/components/grid/stores/datasources/viewV2.ts index 677a85312f..d9cac5397d 100644 --- a/packages/frontend-core/src/components/grid/stores/datasources/viewV2.ts +++ b/packages/frontend-core/src/components/grid/stores/datasources/viewV2.ts @@ -4,23 +4,17 @@ import { SaveRowRequest, SortOrder, UIDatasource, + UIView, UpdateViewRequest, } from "@budibase/types" import { Store as StoreContext } from ".." +import { DatasourceViewActions } from "." const SuppressErrors = true interface ViewActions { viewV2: { - actions: { - saveDefinition: (newDefinition: UpdateViewRequest) => Promise - addRow: (row: SaveRowRequest) => Promise - updateRow: (row: SaveRowRequest) => Promise - deleteRows: (rows: (string | Row)[]) => Promise - getRow: (id: string) => Promise - isDatasourceValid: (datasource: UIDatasource) => boolean - canUseColumn: (name: string) => boolean - } + actions: DatasourceViewActions } } @@ -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) } @@ -154,7 +148,7 @@ export const initialise = (context: StoreContext) => { unsubscribers.push( sort.subscribe(async $sort => { // Ensure we're updating the correct view - const $view = get(definition) + const $view = get(definition) as UIView if ($view?.id !== $datasource.id) { return } @@ -205,7 +199,7 @@ export const initialise = (context: StoreContext) => { await datasource.actions.saveDefinition({ ...$view, queryUI: $filter, - }) + } as never as UpdateViewRequest) // Refresh data since view definition changed await rows.actions.refreshData() diff --git a/packages/frontend-core/src/components/grid/stores/index.ts b/packages/frontend-core/src/components/grid/stores/index.ts index 1ef5da03b6..f2b6c973b9 100644 --- a/packages/frontend-core/src/components/grid/stores/index.ts +++ b/packages/frontend-core/src/components/grid/stores/index.ts @@ -59,11 +59,9 @@ export type Store = BaseStore & Columns.Store & Table.Store & ViewV2.Store & - NonPlus.Store & { + NonPlus.Store & + Datasource.Store & { // TODO while typing the rest of stores - datasource: Writable & { actions: any } - definition: Writable - enrichedSchema: any fetch: Writable filter: Writable inlineFilters: Writable @@ -75,6 +73,9 @@ export type Store = BaseStore & rows: Writable & { actions: any } subscribe: any config: Writable + dispatch: (event: string, data: any) => any + notifications: Writable + schemaOverrides: Writable } export const attachStores = (context: Store): Store => { @@ -106,5 +107,5 @@ export const attachStores = (context: Store): Store => { } } - return context + return context as Store } diff --git a/packages/frontend-core/src/utils/index.js b/packages/frontend-core/src/utils/index.ts similarity index 100% rename from packages/frontend-core/src/utils/index.js rename to packages/frontend-core/src/utils/index.ts diff --git a/packages/frontend-core/src/utils/relatedColumns.js b/packages/frontend-core/src/utils/relatedColumns.js deleted file mode 100644 index 6e7968f70c..0000000000 --- a/packages/frontend-core/src/utils/relatedColumns.js +++ /dev/null @@ -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 -} diff --git a/packages/frontend-core/src/utils/relatedColumns.ts b/packages/frontend-core/src/utils/relatedColumns.ts new file mode 100644 index 0000000000..e7bd3662d3 --- /dev/null +++ b/packages/frontend-core/src/utils/relatedColumns.ts @@ -0,0 +1,129 @@ +import { Helpers } from "@budibase/bbui" +import { + FieldType, + isRelationshipField, + RelationshipType, + Row, + UIFieldSchema, +} from "@budibase/types" + +const columnTypeManyTypeOverrides: Partial> = { + [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 +): Record | undefined { + if (!schema) { + return + } + const result = Object.keys(schema).reduce>( + (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 +} diff --git a/packages/types/src/ui/stores/grid/datasource.ts b/packages/types/src/ui/stores/grid/datasource.ts index d7367352d5..1d9b6740a4 100644 --- a/packages/types/src/ui/stores/grid/datasource.ts +++ b/packages/types/src/ui/stores/grid/datasource.ts @@ -1,5 +1,11 @@ -export interface UIDatasource { +import { UITable, UIView } from "@budibase/types" + +export type UIDatasource = (UITable | UIView) & { type: string - id: string - tableId: string +} + +export interface UIFieldMutation { + visible?: boolean + readonly?: boolean + width?: number } diff --git a/packages/types/src/ui/stores/grid/index.ts b/packages/types/src/ui/stores/grid/index.ts index f6c3472aaa..bcd7e3267d 100644 --- a/packages/types/src/ui/stores/grid/index.ts +++ b/packages/types/src/ui/stores/grid/index.ts @@ -1,2 +1,4 @@ export * from "./columns" export * from "./datasource" +export * from "./table" +export * from "./view" diff --git a/packages/types/src/ui/stores/grid/table.ts b/packages/types/src/ui/stores/grid/table.ts new file mode 100644 index 0000000000..a5a13d5fa2 --- /dev/null +++ b/packages/types/src/ui/stores/grid/table.ts @@ -0,0 +1,34 @@ +import { + BasicViewFieldMetadata, + FieldSchema, + FieldType, + RelationSchemaField, + SortOrder, + Table, + UISearchFilter, +} from "@budibase/types" + +export interface UITable extends Omit { + name: string + id: string + type: string + tableId: string + primaryDisplay?: string + sort?: { + field: string + order: SortOrder + } + queryUI: UISearchFilter + schema: Record +} + +export type UIFieldSchema = FieldSchema & + BasicViewFieldMetadata & { + related?: { field: string; subField: string } + columns?: Record + cellRenderType?: string + } + +interface UIRelationSchemaField extends RelationSchemaField { + type: FieldType +} diff --git a/packages/types/src/ui/stores/grid/view.ts b/packages/types/src/ui/stores/grid/view.ts new file mode 100644 index 0000000000..f81cc34aaf --- /dev/null +++ b/packages/types/src/ui/stores/grid/view.ts @@ -0,0 +1,6 @@ +import { ViewV2 } from "@budibase/types" +import { UIFieldSchema } from "./table" + +export interface UIView extends ViewV2 { + schema: Record +}