1
0
Fork 0
mirror of synced 2024-10-04 03:54:37 +13:00

Merge pull request #5225 from Budibase/cheeks-fixes

Misc fixes
This commit is contained in:
Andrew Kingston 2022-04-11 09:08:01 +01:00 committed by GitHub
commit a5ac60f201
16 changed files with 146 additions and 58 deletions

View file

@ -2,9 +2,15 @@ export default function (url) {
return url return url
.split("/") .split("/")
.map(part => { .map(part => {
// if parameter, then use as is part = decodeURIComponent(part)
if (part.startsWith(":")) return part part = part.replace(/ /g, "-")
return encodeURIComponent(part.replace(/ /g, "-"))
// If parameter, then use as is
if (!part.startsWith(":")) {
part = encodeURIComponent(part)
}
return part
}) })
.join("/") .join("/")
.toLowerCase() .toLowerCase()

View file

@ -6,15 +6,10 @@
export let overlayEnabled = true export let overlayEnabled = true
let imageError = false let imageError = false
let imageLoaded = false
const imageRenderError = () => { const imageRenderError = () => {
imageError = true imageError = true
} }
const imageLoadSuccess = () => {
imageLoaded = true
}
</script> </script>
<div class="template-card" style="background-color:{backgroundColour};"> <div class="template-card" style="background-color:{backgroundColour};">
@ -23,8 +18,7 @@
alt={name} alt={name}
src={imageSrc} src={imageSrc}
on:error={imageRenderError} on:error={imageRenderError}
on:load={imageLoadSuccess} class:error={imageError}
class={`${imageLoaded ? "loaded" : ""}`}
/> />
<div style={`display:${imageError ? "block" : "none"}`}> <div style={`display:${imageError ? "block" : "none"}`}>
<svg <svg
@ -104,15 +98,14 @@
width: 100%; width: 100%;
} }
.template-card img.loaded {
display: block;
}
.template-card img { .template-card img {
display: none; display: block;
max-width: 100%; max-width: 100%;
border-radius: var(--border-radius-s) 0px var(--border-radius-s) 0px; border-radius: var(--border-radius-s) 0px var(--border-radius-s) 0px;
} }
.template-card img.error {
display: none;
}
.template-card:hover { .template-card:hover {
background: var(--spectrum-alias-background-color-tertiary); background: var(--spectrum-alias-background-color-tertiary);

View file

@ -3,13 +3,14 @@
import PathTree from "./PathTree.svelte" import PathTree from "./PathTree.svelte"
let routes = {} let routes = {}
$: paths = Object.keys(routes || {}).sort() let paths = []
$: { $: allRoutes = $store.routes
const allRoutes = $store.routes $: selectedScreenId = $store.selectedScreenId
$: updatePaths(allRoutes, $selectedAccessRole, selectedScreenId)
const updatePaths = (allRoutes, selectedRoleId, selectedScreenId) => {
const sortedPaths = Object.keys(allRoutes || {}).sort() const sortedPaths = Object.keys(allRoutes || {}).sort()
const selectedRoleId = $selectedAccessRole
const selectedScreenId = $store.selectedScreenId
let found = false let found = false
let firstValidScreenId let firstValidScreenId
@ -41,11 +42,15 @@
}) })
}) })
}) })
routes = filteredRoutes routes = { ...filteredRoutes }
paths = Object.keys(routes || {}).sort()
// Select the correct role for the current screen ID // Select the correct role for the current screen ID
if (!found && screenRoleId) { if (!found && screenRoleId) {
selectedAccessRole.set(screenRoleId) selectedAccessRole.set(screenRoleId)
if (screenRoleId !== selectedRoleId) {
updatePaths(allRoutes, screenRoleId, selectedScreenId)
}
} }
// If the selected screen isn't in this filtered list, select the first one // If the selected screen isn't in this filtered list, select the first one

View file

@ -19,7 +19,7 @@
.filter(a => a.definition.trigger?.stepId === "APP") .filter(a => a.definition.trigger?.stepId === "APP")
.map(automation => { .map(automation => {
const schema = Object.entries( const schema = Object.entries(
automation.definition.trigger.inputs.fields automation.definition.trigger.inputs.fields || {}
).map(([name, type]) => ({ name, type })) ).map(([name, type]) => ({ name, type }))
return { return {

View file

@ -3,6 +3,7 @@
import { roles } from "stores/backend" import { roles } from "stores/backend"
export let value export let value
export let error
</script> </script>
<Select <Select
@ -11,4 +12,5 @@
options={$roles} options={$roles}
getOptionLabel={role => role.name} getOptionLabel={role => role.name}
getOptionValue={role => role._id} getOptionValue={role => role._id}
{error}
/> />

View file

@ -8,14 +8,50 @@
import { currentAsset, store } from "builderStore" import { currentAsset, store } from "builderStore"
import { FrontendTypes } from "constants" import { FrontendTypes } from "constants"
import sanitizeUrl from "builderStore/store/screenTemplates/utils/sanitizeUrl" import sanitizeUrl from "builderStore/store/screenTemplates/utils/sanitizeUrl"
import { allScreens, selectedAccessRole } from "builderStore"
export let componentInstance export let componentInstance
export let bindings export let bindings
function setAssetProps(name, value, parser) { let errors = {}
if (parser && typeof parser === "function") {
const routeTaken = url => {
const roleId = get(selectedAccessRole) || "BASIC"
return get(allScreens).some(
screen =>
screen.routing.route.toLowerCase() === url.toLowerCase() &&
screen.routing.roleId === roleId
)
}
const roleTaken = roleId => {
const url = get(currentAsset)?.routing.route
return get(allScreens).some(
screen =>
screen.routing.route.toLowerCase() === url.toLowerCase() &&
screen.routing.roleId === roleId
)
}
const setAssetProps = (name, value, parser, validate) => {
if (parser) {
value = parser(value) value = parser(value)
} }
if (validate) {
const error = validate(value)
errors = {
...errors,
[name]: error,
}
if (error) {
return
}
} else {
errors = {
...errors,
[name]: null,
}
}
const selectedAsset = get(currentAsset) const selectedAsset = get(currentAsset)
store.update(state => { store.update(state => {
@ -38,7 +74,6 @@
} }
const screenSettings = [ const screenSettings = [
// { key: "description", label: "Description", control: Input },
{ {
key: "routing.route", key: "routing.route",
label: "Route", label: "Route",
@ -49,8 +84,26 @@
} }
return sanitizeUrl(val) return sanitizeUrl(val)
}, },
validate: val => {
const exisingValue = get(currentAsset)?.routing.route
if (val !== exisingValue && routeTaken(val)) {
return "That URL is already in use for this role"
}
return null
},
},
{
key: "routing.roleId",
label: "Access",
control: RoleSelect,
validate: val => {
const exisingValue = get(currentAsset)?.routing.roleId
if (val !== exisingValue && roleTaken(val)) {
return "That role is already in use for this URL"
}
return null
},
}, },
{ key: "routing.roleId", label: "Access", control: RoleSelect },
{ key: "layoutId", label: "Layout", control: LayoutSelect }, { key: "layoutId", label: "Layout", control: LayoutSelect },
] ]
</script> </script>
@ -62,9 +115,11 @@
control={def.control} control={def.control}
label={def.label} label={def.label}
key={def.key} key={def.key}
error="asdasds"
value={deepGet($currentAsset, def.key)} value={deepGet($currentAsset, def.key)}
onChange={val => setAssetProps(def.key, val, def.parser)} onChange={val => setAssetProps(def.key, val, def.parser, def.validate)}
{bindings} {bindings}
props={{ error: errors[def.key] }}
/> />
{/each} {/each}
</DetailSummary> </DetailSummary>

View file

@ -15,7 +15,7 @@
import { onMount } from "svelte" import { onMount } from "svelte"
import { templates } from "stores/portal" import { templates } from "stores/portal"
let loaded = false let loaded = $templates?.length
let template let template
let creationModal = false let creationModal = false
let creatingApp = false let creatingApp = false

View file

@ -40,7 +40,7 @@
let unpublishModal let unpublishModal
let iconModal let iconModal
let creatingApp = false let creatingApp = false
let loaded = false let loaded = $apps?.length || $templates?.length
let searchTerm = "" let searchTerm = ""
let cloud = $admin.cloud let cloud = $admin.cloud
let appName = "" let appName = ""
@ -292,8 +292,8 @@
<div class="title"> <div class="title">
<div class="welcome"> <div class="welcome">
<Layout noPadding gap="XS"> <Layout noPadding gap="XS">
<Heading size="M">{welcomeHeader}</Heading> <Heading size="L">{welcomeHeader}</Heading>
<Body size="S"> <Body size="M">
{welcomeBody} {welcomeBody}
</Body> </Body>
</Layout> </Layout>
@ -301,7 +301,7 @@
<div class="buttons"> <div class="buttons">
<Button <Button
dataCy="create-app-btn" dataCy="create-app-btn"
size="L" size="M"
icon="Add" icon="Add"
cta cta
on:click={initiateAppCreation} on:click={initiateAppCreation}
@ -311,7 +311,7 @@
{#if $apps?.length > 0} {#if $apps?.length > 0}
<Button <Button
icon="Experience" icon="Experience"
size="L" size="M"
quiet quiet
secondary secondary
on:click={$goto("/builder/portal/apps/templates")} on:click={$goto("/builder/portal/apps/templates")}
@ -348,7 +348,7 @@
{#if enrichedApps.length} {#if enrichedApps.length}
<Layout noPadding gap="S"> <Layout noPadding gap="S">
<div class="title"> <div class="title">
<Detail size="L">My apps</Detail> <Detail size="L">Apps</Detail>
{#if enrichedApps.length > 1} {#if enrichedApps.length > 1}
<div class="app-actions"> <div class="app-actions">
{#if cloud} {#if cloud}

View file

@ -5,7 +5,7 @@
import { onMount } from "svelte" import { onMount } from "svelte"
import { templates } from "stores/portal" import { templates } from "stores/portal"
let loaded = false let loaded = $templates?.length
onMount(async () => { onMount(async () => {
try { try {

View file

@ -102,7 +102,7 @@
white-space: nowrap; white-space: nowrap;
} }
.spectrum-Card-footer { .spectrum-Card-footer {
word-wrap: anywhere; word-wrap: break-word;
white-space: pre-wrap; white-space: pre-wrap;
} }
.horizontal .spectrum-Card-coverPhoto { .horizontal .spectrum-Card-coverPhoto {

View file

@ -125,7 +125,11 @@
{#if schemaLoaded} {#if schemaLoaded}
<Block> <Block>
<div class="card-list" use:styleable={$component.styles}> <div class="card-list" use:styleable={$component.styles}>
<BlockComponent type="form" bind:id={formId} props={{ dataSource }}> <BlockComponent
type="form"
bind:id={formId}
props={{ dataSource, disableValidation: true }}
>
{#if title || enrichedSearchColumns?.length || showTitleButton} {#if title || enrichedSearchColumns?.length || showTitleButton}
<div class="header" class:mobile={$context.device.mobile}> <div class="header" class:mobile={$context.device.mobile}>
<div class="title"> <div class="title">

View file

@ -106,7 +106,11 @@
{#if schemaLoaded} {#if schemaLoaded}
<Block> <Block>
<div class={size} use:styleable={$component.styles}> <div class={size} use:styleable={$component.styles}>
<BlockComponent type="form" bind:id={formId} props={{ dataSource }}> <BlockComponent
type="form"
bind:id={formId}
props={{ dataSource, disableValidation: true }}
>
{#if title || enrichedSearchColumns?.length || showTitleButton} {#if title || enrichedSearchColumns?.length || showTitleButton}
<div class="header" class:mobile={$context.device.mobile}> <div class="header" class:mobile={$context.device.mobile}>
<div class="title"> <div class="title">

View file

@ -34,7 +34,7 @@
color: var(--spectrum-global-color-gray-700) !important; color: var(--spectrum-global-color-gray-700) !important;
} }
div :global(.apexcharts-datalabel) { div :global(.apexcharts-datalabel) {
fill: var(--spectrum-global-color-gray-800); fill: white;
} }
div :global(.apexcharts-tooltip) { div :global(.apexcharts-tooltip) {
background-color: var(--spectrum-global-color-gray-200) !important; background-color: var(--spectrum-global-color-gray-200) !important;
@ -45,4 +45,12 @@
background-color: var(--spectrum-global-color-gray-100) !important; background-color: var(--spectrum-global-color-gray-100) !important;
border-color: var(--spectrum-global-color-gray-300) !important; border-color: var(--spectrum-global-color-gray-300) !important;
} }
div :global(.apexcharts-theme-dark .apexcharts-tooltip-text) {
color: white;
}
div
:global(.apexcharts-theme-dark
.apexcharts-tooltip-series-group.apexcharts-active) {
padding-bottom: 0;
}
</style> </style>

View file

@ -9,6 +9,10 @@
export let disabled = false export let disabled = false
export let actionType = "Create" export let actionType = "Create"
// Not exposed as a builder setting. Used internally to disable validation
// for fields rendered in things like search blocks.
export let disableValidation = false
const context = getContext("context") const context = getContext("context")
const { API, fetchDatasourceSchema } = getContext("sdk") const { API, fetchDatasourceSchema } = getContext("sdk")
@ -102,6 +106,7 @@
{schema} {schema}
{table} {table}
{initialValues} {initialValues}
{disableValidation}
> >
<slot /> <slot />
</InnerForm> </InnerForm>

View file

@ -10,6 +10,7 @@
export let size export let size
export let schema export let schema
export let table export let table
export let disableValidation = false
const component = getContext("component") const component = getContext("component")
const { styleable, Provider, ActionTypes } = getContext("sdk") const { styleable, Provider, ActionTypes } = getContext("sdk")
@ -141,12 +142,14 @@
// Create validation function based on field schema // Create validation function based on field schema
const schemaConstraints = schema?.[field]?.constraints const schemaConstraints = schema?.[field]?.constraints
const validator = createValidatorFromConstraints( const validator = disableValidation
schemaConstraints, ? null
validationRules, : createValidatorFromConstraints(
field, schemaConstraints,
table validationRules,
) field,
table
)
// If we've already registered this field then keep some existing state // If we've already registered this field then keep some existing state
let initialValue = Helpers.deepGet(initialValues, field) ?? defaultValue let initialValue = Helpers.deepGet(initialValues, field) ?? defaultValue
@ -164,7 +167,7 @@
// If this field has already been registered and we previously had an // If this field has already been registered and we previously had an
// error set, then re-run the validator to see if we can unset it // error set, then re-run the validator to see if we can unset it
if (fieldState.error) { if (fieldState.error) {
initialError = validator(initialValue) initialError = validator?.(initialValue)
} }
} }
@ -254,7 +257,7 @@
} }
// Update field state // Update field state
const error = validator ? validator(value) : null const error = validator?.(value)
fieldInfo.update(state => { fieldInfo.update(state => {
state.fieldState.value = value state.fieldState.value = value
state.fieldState.error = error state.fieldState.error = error
@ -288,12 +291,14 @@
// Create new validator // Create new validator
const schemaConstraints = schema?.[field]?.constraints const schemaConstraints = schema?.[field]?.constraints
const validator = createValidatorFromConstraints( const validator = disableValidation
schemaConstraints, ? null
validationRules, : createValidatorFromConstraints(
field, schemaConstraints,
table validationRules,
) field,
table
)
// Update validator // Update validator
fieldInfo.update(state => { fieldInfo.update(state => {

View file

@ -329,13 +329,13 @@ export const enrichButtonActions = (actions, context) => {
return actions return actions
} }
// Button context is built up as actions are executed.
// Inherit any previous button context which may have come from actions
// before a confirmable action since this breaks the chain.
let buttonContext = context.actions || []
const handlers = actions.map(def => handlerMap[def["##eventHandlerType"]]) const handlers = actions.map(def => handlerMap[def["##eventHandlerType"]])
return async eventContext => { return async eventContext => {
// Button context is built up as actions are executed.
// Inherit any previous button context which may have come from actions
// before a confirmable action since this breaks the chain.
let buttonContext = context.actions || []
for (let i = 0; i < handlers.length; i++) { for (let i = 0; i < handlers.length; i++) {
try { try {
// Skip any non-existent action definitions // Skip any non-existent action definitions
@ -346,6 +346,7 @@ export const enrichButtonActions = (actions, context) => {
// Built total context for this action // Built total context for this action
const totalContext = { const totalContext = {
...context, ...context,
state: get(stateStore),
actions: buttonContext, actions: buttonContext,
eventContext, eventContext,
} }