This commit is contained in:
Gerard Burns 2024-04-11 10:56:08 +01:00
parent cd36056124
commit 293a969f5f
9 changed files with 160 additions and 116 deletions

View File

@ -1,6 +1,6 @@
<script> <script>
import ExplanationModal from './ExplanationModal/index.svelte' import ExplanationModal from './ExplanationModal/index.svelte'
import { warnings, errors } from "./validator"; import { messages } from "./columnInfo";
import { Column, Support, NotRequired, StringNumber } from "./lines" import { Column, Support, NotRequired, StringNumber } from "./lines"
import subjects from './subjects'; import subjects from './subjects';
@ -30,23 +30,23 @@
class="tooltipContents" class="tooltipContents"
> >
<Column <Column
{columnName} {columnName}
{columnIcon} {columnIcon}
{columnType} {columnType}
{tableHref} {tableHref}
{setExplanationSubject} {setExplanationSubject}
/> />
<Support <Support
{support} support={support.support}
{setExplanationSubject} {setExplanationSubject}
/> />
{#if support?.warnings?.includes(warnings.stringAsNumber)} {#if support?.messages?.includes(messages.stringAsNumber)}
<StringNumber <StringNumber
{setExplanationSubject} {setExplanationSubject}
/> />
{/if} {/if}
{#if support?.warnings?.includes(warnings.notRequired)} {#if support?.messages?.includes(messages.notRequired)}
<NotRequired <NotRequired
{setExplanationSubject} {setExplanationSubject}
/> />

View File

@ -0,0 +1,77 @@
import { capitalize } from 'lodash';
export const messages = {
jsonPrimitivesOnly: Symbol("column-info-json-primitives-only"),
stringAsNumber: Symbol("column-info-string-as-number"),
chartDatetime: Symbol("column-info-chart-datetime"),
notRequired: Symbol("column-info-not-required"),
contextError: Symbol("column-info-context-error"),
}
export const support = {
unsupported: Symbol("column-info-unsupported"),
partialSupport: Symbol("column-info-partialSupport"),
supported: Symbol("column-info-supported")
}
const getSupport = (type, columnInfo) => {
if (!columnInfo?.typeSupport) {
return support.supported
}
if (columnInfo?.typeSupport?.supported?.find(mapping => mapping === type || mapping?.type === type)) {
return support.supported;
}
if (columnInfo?.typeSupport?.partialSupport?.find(mapping => mapping === type || mapping?.type === type)) {
return support.partialSupport;
}
return support.unsupported
}
const getSupportMessage = (type, columnInfo) => {
if (!columnInfo?.typeSupport) {
return null
}
const supported = columnInfo?.typeSupport?.supported?.find(mapping => mapping?.type === type)
if (supported) {
return messages[supported?.message]
}
const partialSupport = columnInfo?.typeSupport?.partialSupport?.find(mapping => mapping?.type === type)
if (partialSupport) {
return messages[partialSupport?.message]
}
const unsupported = columnInfo?.typeSupport?.unsupported?.find(mapping => mapping?.type === type)
if (unsupported) {
return messages[unsupported?.message]
}
return null
}
export const getColumnInfoMessagesAndSupport = (fieldSchema, columnInfo) => {
try {
const columnInfoMessagesAndSupport = {
support: getSupport(fieldSchema.type, columnInfo),
messages: [getSupportMessage(fieldSchema.type, columnInfo)],
}
const isRequired = fieldSchema?.constraints?.presence?.allowEmpty === false
if (!isRequired) {
columnInfoMessagesAndSupport.messages.push(messages.notRequired);
}
return columnInfoMessagesAndSupport;
} catch (e) {
return {
support: support.partialSupport,
messages: [messages.contextError]
}
}
}

View File

@ -1,2 +1,2 @@
export { default as FieldContext } from "./FieldContext.svelte" export { default as FieldContext } from "./FieldContext.svelte"
export * from "./validator" export * from "./columnInfo"

View File

@ -1,18 +1,53 @@
<script> <script>
import { Line, InfoWord, DocumentationLink, Space, Text, Period } from "../typography" import { Line, InfoWord, DocumentationLink, Space, Text, Period } from "../typography"
import subjects from '../subjects' import subjects from '../subjects'
import * as columnInfo from '../columnInfo'
export let setExplanationSubject export let setExplanationSubject
export let support export let support
const getIcon = (support) => {
if (support === columnInfo.support.unsupported) {
return "Alert"
} else if (support === columnInfo.support.supported) {
return "CheckmarkCircle"
}
return "AlertCheck"
}
const getColor = (support) => {
if (support === columnInfo.support.unsupported) {
return "var(--red)"
} else if (support === columnInfo.support.supported) {
return "var(--green)"
}
return "var(--yellow)"
}
const getText = (support) => {
if (support === columnInfo.support.unsupported) {
return "Not compatible"
} else if (support === columnInfo.support.supported) {
return "Compatible"
}
return "Partially compatible"
}
$: icon = getIcon(support);
$: color = getColor(support);
$: text = getText(support);
</script> </script>
<Line> <Line>
<InfoWord <InfoWord
on:mouseenter={() => setExplanationSubject(subjects.support)} on:mouseenter={() => setExplanationSubject(subjects.support)}
on:mouseleave={() => setExplanationSubject(subjects.none)} on:mouseleave={() => setExplanationSubject(subjects.none)}
icon={support.icon} {icon}
color={support.iconColor} {color}
text={support.text} {text}
/> />
<Space /> <Space />
<Text>with</Text> <Text>with</Text>

View File

@ -1,80 +0,0 @@
import { capitalize } from 'lodash';
export const errors = {
general: Symbol("values-validator-general"),
jsonPrimitivesOnly: Symbol("values-validator-json-primitives-only"),
}
export const warnings = {
stringAsNumber: Symbol("values-validator-string-as-number"),
chartDatetime: Symbol("values-validator-chart-datetime"),
notRequired: Symbol("values-validator-not-required"),
}
export const constants = {
unsupported: Symbol("values-validator-unsupported"),
partialSupport: Symbol("values-validator-partialSupport"),
supported: Symbol("values-validator-supported")
}
export const validate = (fieldSchema) => {
try {
const response = {
level: null,
warnings: [],
errors: [],
text: "",
icon: "",
iconColor: ""
}
const generalUnsupportedFields = ["array", "attachment", "barcodeqr", "link", "bb_reference"]
if (generalUnsupportedFields.includes(fieldSchema.type)) {
response.errors.push(errors.general)
}
if (fieldSchema.type === "json") {
response.errors.push(errors.jsonPrimitivesOnly)
}
if (fieldSchema.type === "string") {
response.warnings.push(warnings.stringAsNumber)
}
if (fieldSchema.type === "datetime") {
response.warnings.push(warnings.chartDatetime);
//"This column can be used as an input for a chart, but it may be parsed differently depending on which is used.
}
const isRequired = fieldSchema?.constraints?.presence?.allowEmpty === false
if (!isRequired) {
response.warnings.push(warnings.notRequired);
}
if (response.errors.length > 0) {
response.level = constants.unsupported
response.text = "Not compatible"
response.icon = "Alert"
response.iconColor = "var(--red)"
} else if (response.warnings.length > 0) {
response.level = constants.partialSupport
response.text = "Partially compatible"
response.icon = "AlertCheck"
response.iconColor = "var(--yellow)"
} else {
response.level = constants.supported
response.text = "Compatible"
response.icon = "CheckmarkCircle"
response.iconColor = "var(--green)"
}
return response
} catch (e) {
return {
level: constants.partialSupport,
warnings: [],
errors: [],
text: "Partially compatible",
icon: "AlertCheck",
iconColor: "var(--yellow)"
}
}
}

View File

@ -3,7 +3,7 @@
import { getDatasourceForProvider, getSchemaForDatasource } from "dataBinding" import { getDatasourceForProvider, getSchemaForDatasource } from "dataBinding"
import { selectedScreen } from "stores/builder" import { selectedScreen } from "stores/builder"
import { createEventDispatcher } from "svelte" import { createEventDispatcher } from "svelte"
import { FieldContext, validate } from './FieldContext' import { FieldContext, getColumnInfoMessagesAndSupport } from './FieldContext'
import { debounce } from "lodash" import { debounce } from "lodash"
import { goto, params } from "@roxi/routify" import { goto, params } from "@roxi/routify"
import { Constants } from "@budibase/frontend-core" import { Constants } from "@budibase/frontend-core"
@ -12,21 +12,21 @@
export let componentInstance = {} export let componentInstance = {}
export let value = "" export let value = ""
export let placeholder export let placeholder
export let columnContext export let columnInfo
let contextTooltipAnchor = null let contextTooltipAnchor = null
let currentOption = null let currentOption = null
let previousOption = null let previousOption = null
let contextTooltipVisible = false let contextTooltipVisible = false
const getFieldSupport = (schema, columnContext) => { const getFieldSupport = (schema, columnInfo) => {
if (!columnContext) { if (columnInfo == null) {
return {} return {}
} }
const fieldSupport = {} const fieldSupport = {}
Object.entries(schema || {}).forEach(([key, value]) => { Object.entries(schema || {}).forEach(([key, value]) => {
fieldSupport[key] = validate(value) fieldSupport[key] = getColumnInfoMessagesAndSupport(value)
}) })
return fieldSupport return fieldSupport
@ -35,7 +35,7 @@
const dispatch = createEventDispatcher() const dispatch = createEventDispatcher()
$: datasource = getDatasourceForProvider($selectedScreen, componentInstance) $: datasource = getDatasourceForProvider($selectedScreen, componentInstance)
$: schema = getSchemaForDatasource($selectedScreen, datasource).schema $: schema = getSchemaForDatasource($selectedScreen, datasource).schema
$: fieldSupport = getFieldSupport(schema, columnContext); $: fieldSupport = getFieldSupport(schema, columnInfo);
$: options = Object.keys(schema || {}) $: options = Object.keys(schema || {})
$: boundValue = getValidValue(value, options) $: boundValue = getValidValue(value, options)
@ -117,7 +117,7 @@
{onOptionMouseleave} {onOptionMouseleave}
/> />
{#if columnContext} {#if columnInfo}
<ContextTooltip <ContextTooltip
visible={contextTooltipVisible} visible={contextTooltipVisible}
anchor={contextTooltipAnchor} anchor={contextTooltipAnchor}

View File

@ -5,7 +5,7 @@
componentStore, componentStore,
} from "stores/builder" } from "stores/builder"
import { createEventDispatcher } from "svelte" import { createEventDispatcher } from "svelte"
import { FieldContext, validate } from './FieldContext' import { FieldContext, getColumnInfoMessagesAndSupport } from './FieldContext'
import { FIELDS } from 'constants/backend' import { FIELDS } from 'constants/backend'
import { goto, params } from "@roxi/routify" import { goto, params } from "@roxi/routify"
import { debounce } from "lodash" import { debounce } from "lodash"
@ -14,8 +14,7 @@
export let componentInstance = {} export let componentInstance = {}
export let value = "" export let value = ""
export let placeholder export let placeholder
export let columnContext export let columnInfo
export let valueTypes
let contextTooltipAnchor = null let contextTooltipAnchor = null
let currentOption = null let currentOption = null
@ -26,14 +25,15 @@
componentInstance?._component componentInstance?._component
) )
const getFieldSupport = (schema, columnContext) => { const getFieldSupport = (schema, columnInfo) => {
if (!columnContext) { if (columnInfo == null) {
return {} return {}
} }
const fieldSupport = {} const fieldSupport = {}
Object.entries(schema || {}).forEach(([key, value]) => { Object.entries(schema || {}).forEach(([key, value]) => {
fieldSupport[key] = validate(value) // super TODO: nicer to do this at the component level jit and store each value seperately so i don't have this long ass name???
fieldSupport[key] = getColumnInfoMessagesAndSupport(value, columnInfo)
}) })
return fieldSupport return fieldSupport
@ -43,9 +43,11 @@
$: datasource = getDatasourceForProvider($selectedScreen, componentInstance) $: datasource = getDatasourceForProvider($selectedScreen, componentInstance)
$: schema = getSchemaForDatasource($selectedScreen, datasource).schema $: schema = getSchemaForDatasource($selectedScreen, datasource).schema
$: options = Object.keys(schema || {}) $: options = Object.keys(schema || {})
$: fieldSupport = getFieldSupport(schema, columnContext); $: fieldSupport = getFieldSupport(schema, columnInfo);
$: boundValue = getValidOptions(value, options) $: boundValue = getValidOptions(value, options)
$: console.log(fieldSupport)
const getValidOptions = (selectedOptions, allOptions) => { const getValidOptions = (selectedOptions, allOptions) => {
// Fix the hardcoded default string value // Fix the hardcoded default string value
@ -133,7 +135,7 @@
{onOptionMouseleave} {onOptionMouseleave}
/> />
{#if columnContext} {#if columnInfo}
<ContextTooltip <ContextTooltip
visible={contextTooltipVisible} visible={contextTooltipVisible}
anchor={contextTooltipAnchor} anchor={contextTooltipAnchor}

View File

@ -193,7 +193,7 @@
max: setting.max ?? null, max: setting.max ?? null,
// Field select settings // Field select settings
columnContext: setting.columnContext, columnInfo: setting.columnInfo,
valueTypes: setting.valueTypes valueTypes: setting.valueTypes
}} }}
{bindings} {bindings}

View File

@ -1634,8 +1634,18 @@
"label": "Data columns", "label": "Data columns",
"key": "valueColumns", "key": "valueColumns",
"dependsOn": "dataProvider", "dependsOn": "dataProvider",
"valueTypes": ["number"], "columnInfo": {
"columnContext": true, "typeSupport": {
"supported": ["number", "boolean"],
"partialSupport": [
{ "type": "string", "message": "stringAsNumber" },
{ "type": "datetime", "message": "datetimeChart"}
],
"unsupported": [
{ "type": "json", "message": "jsonPrimitivesOnly" }
]
}
},
"required": true "required": true
}, },
{ {