Merge branch 'master' of github.com:Budibase/budibase into feature/frontend-js-performance

This commit is contained in:
Michael Drury 2025-01-24 11:25:12 +00:00
commit 65f9c26666
8 changed files with 72 additions and 43 deletions

View File

@ -293,7 +293,7 @@
type: RowSelector, type: RowSelector,
props: { props: {
row: inputData["oldRow"] || { row: inputData["oldRow"] || {
tableId: inputData["row"].tableId, tableId: inputData["row"]?.tableId,
}, },
meta: { meta: {
fields: inputData["meta"]?.oldFields || {}, fields: inputData["meta"]?.oldFields || {},

View File

@ -9,3 +9,4 @@ export {
lowercase, lowercase,
isBuilderInputFocused, isBuilderInputFocused,
} from "./helpers" } from "./helpers"
export * as featureFlag from "./featureFlags"

View File

@ -1,7 +1,12 @@
import { Component, Screen, ScreenProps } from "@budibase/types" import { Component, Screen, ScreenProps } from "@budibase/types"
import clientManifest from "@budibase/client/manifest.json" import clientManifest from "@budibase/client/manifest.json"
export function findComponentsBySettingsType(screen: Screen, type: string) { export function findComponentsBySettingsType(
screen: Screen,
type: string | string[]
) {
const typesArray = Array.isArray(type) ? type : [type]
const result: { const result: {
component: Component component: Component
setting: { setting: {
@ -9,10 +14,7 @@ export function findComponentsBySettingsType(screen: Screen, type: string) {
key: string key: string
} }
}[] = [] }[] = []
function recurseFieldComponentsInChildren( function recurseFieldComponentsInChildren(component: ScreenProps) {
component: ScreenProps,
type: string
) {
if (!component) { if (!component) {
return return
} }
@ -20,7 +22,7 @@ export function findComponentsBySettingsType(screen: Screen, type: string) {
const definition = getManifestDefinition(component) const definition = getManifestDefinition(component)
const setting = const setting =
"settings" in definition && "settings" in definition &&
definition.settings.find((s: any) => s.type === type) definition.settings.find((s: any) => typesArray.includes(s.type))
if (setting && "type" in setting) { if (setting && "type" in setting) {
result.push({ result.push({
component, component,
@ -28,11 +30,11 @@ export function findComponentsBySettingsType(screen: Screen, type: string) {
}) })
} }
component._children?.forEach(child => { component._children?.forEach(child => {
recurseFieldComponentsInChildren(child, type) recurseFieldComponentsInChildren(child)
}) })
} }
recurseFieldComponentsInChildren(screen?.props, type) recurseFieldComponentsInChildren(screen?.props)
return result return result
} }

View File

@ -3,51 +3,67 @@ import { tables } from "./tables"
import { selectedScreen } from "./screens" import { selectedScreen } from "./screens"
import { viewsV2 } from "./viewsV2" import { viewsV2 } from "./viewsV2"
import { findComponentsBySettingsType } from "@/helpers/screen" import { findComponentsBySettingsType } from "@/helpers/screen"
import { Screen, Table, ViewV2 } from "@budibase/types" import { UIDatasourceType, Screen } from "@budibase/types"
import { queries } from "./queries"
import { views } from "./views"
import { featureFlag } from "@/helpers"
function reduceBy<TItem extends {}, TKey extends keyof TItem>(
key: TKey,
list: TItem[]
) {
return list.reduce(
(result, item) => ({
...result,
[item[key] as string]: item,
}),
{}
)
}
const friendlyNameByType: Partial<Record<UIDatasourceType, string>> = {
viewV2: "view",
}
const validationKeyByType: Record<UIDatasourceType, string | null> = {
table: "tableId",
view: "name",
viewV2: "id",
query: "_id",
custom: null,
}
export const screenComponentErrors = derived( export const screenComponentErrors = derived(
[selectedScreen, tables, viewsV2], [selectedScreen, tables, views, viewsV2, queries],
([$selectedScreen, $tables, $viewsV2]): Record<string, string[]> => { ([$selectedScreen, $tables, $views, $viewsV2, $queries]): Record<
function flattenTablesAndViews(tables: Table[], views: ViewV2[]) { string,
return { string[]
...tables.reduce( > => {
(list, table) => ({ if (!featureFlag.isEnabled("CHECK_SCREEN_COMPONENT_SETTINGS_ERRORS")) {
...list, return {}
[table._id!]: table,
}),
{}
),
...views.reduce(
(list, view) => ({
...list,
[view.id]: view,
}),
{}
),
}
} }
function getInvalidDatasources( function getInvalidDatasources(
screen: Screen, screen: Screen,
datasources: Record<string, any> datasources: Record<string, any>
) { ) {
const friendlyNameByType = {
table: "table",
view: "view",
viewV2: "view",
}
const result: Record<string, string[]> = {} const result: Record<string, string[]> = {}
for (const { component, setting } of findComponentsBySettingsType( for (const { component, setting } of findComponentsBySettingsType(
screen, screen,
"table" ["table", "dataSource"]
)) { )) {
const { resourceId, type, label } = component[setting.key] const componentSettings = component[setting.key]
const { label } = componentSettings
const type = componentSettings.type as UIDatasourceType
const validationKey = validationKeyByType[type]
if (!validationKey) {
continue
}
const resourceId = componentSettings[validationKey]
if (!datasources[resourceId]) { if (!datasources[resourceId]) {
const friendlyTypeName = const friendlyTypeName = friendlyNameByType[type] ?? type
friendlyNameByType[type as keyof typeof friendlyNameByType]
result[component._id!] = [ result[component._id!] = [
`The ${friendlyTypeName} named "${label}" does not exist`, `The ${friendlyTypeName} named "${label}" could not be found`,
] ]
} }
} }
@ -55,7 +71,13 @@ export const screenComponentErrors = derived(
return result return result
} }
const datasources = flattenTablesAndViews($tables.list, $viewsV2.list) const datasources = {
...reduceBy("_id", $tables.list),
...reduceBy("name", $views.list),
...reduceBy("id", $viewsV2.list),
...reduceBy("_id", $queries.list),
}
return getInvalidDatasources($selectedScreen, datasources) return getInvalidDatasources($selectedScreen, datasources)
} }
) )

View File

@ -38,7 +38,7 @@
.component-placeholder { .component-placeholder {
display: flex; display: flex;
flex-direction: row; flex-direction: row;
justify-content: flex-start; justify-content: center;
align-items: center; align-items: center;
color: var(--spectrum-global-color-gray-600); color: var(--spectrum-global-color-gray-600);
font-size: var(--font-size-s); font-size: var(--font-size-s);

View File

@ -1,5 +1,6 @@
export enum FeatureFlag { export enum FeatureFlag {
USE_ZOD_VALIDATOR = "USE_ZOD_VALIDATOR", USE_ZOD_VALIDATOR = "USE_ZOD_VALIDATOR",
CHECK_SCREEN_COMPONENT_SETTINGS_ERRORS = "CHECK_SCREEN_COMPONENT_SETTINGS_ERRORS",
// Account-portal // Account-portal
DIRECT_LOGIN_TO_ACCOUNT_PORTAL = "DIRECT_LOGIN_TO_ACCOUNT_PORTAL", DIRECT_LOGIN_TO_ACCOUNT_PORTAL = "DIRECT_LOGIN_TO_ACCOUNT_PORTAL",
@ -7,6 +8,7 @@ export enum FeatureFlag {
export const FeatureFlagDefaults = { export const FeatureFlagDefaults = {
[FeatureFlag.USE_ZOD_VALIDATOR]: false, [FeatureFlag.USE_ZOD_VALIDATOR]: false,
[FeatureFlag.CHECK_SCREEN_COMPONENT_SETTINGS_ERRORS]: false,
// Account-portal // Account-portal
[FeatureFlag.DIRECT_LOGIN_TO_ACCOUNT_PORTAL]: false, [FeatureFlag.DIRECT_LOGIN_TO_ACCOUNT_PORTAL]: false,

View File

@ -0,0 +1 @@
export type UIDatasourceType = "table" | "view" | "viewV2" | "query" | "custom"

View File

@ -2,3 +2,4 @@ export * from "./stores"
export * from "./bindings" export * from "./bindings"
export * from "./components" export * from "./components"
export * from "./dataFetch" export * from "./dataFetch"
export * from "./datasource"