Merge branch 'develop' of github.com:Budibase/budibase into merge/master-develop-29-06-23

This commit is contained in:
mike12345567 2023-06-30 11:37:03 +01:00
commit 488e7b9502
20 changed files with 142 additions and 57 deletions

View File

@ -71,6 +71,7 @@
timeOnly, timeOnly,
enableTime, enableTime,
time24hr, time24hr,
disabled,
} }
const handleChange = event => { const handleChange = event => {

View File

@ -155,6 +155,7 @@ export const getFrontendStore = () => {
...INITIAL_FRONTEND_STATE.features, ...INITIAL_FRONTEND_STATE.features,
...application.features, ...application.features,
}, },
icon: application.icon || {},
initialised: true, initialised: true,
})) }))
screenHistoryStore.reset() screenHistoryStore.reset()
@ -235,6 +236,7 @@ export const getFrontendStore = () => {
legalDirectChildren = [] legalDirectChildren = []
) => { ) => {
const type = component._component const type = component._component
if (illegalChildren.includes(type)) { if (illegalChildren.includes(type)) {
return type return type
} }
@ -248,10 +250,13 @@ export const getFrontendStore = () => {
return return
} }
if (type === "@budibase/standard-components/sidepanel") {
illegalChildren = []
}
const definition = store.actions.components.getDefinition( const definition = store.actions.components.getDefinition(
component._component component._component
) )
// Reset whitelist for direct children // Reset whitelist for direct children
legalDirectChildren = [] legalDirectChildren = []
if (definition?.legalDirectChildren?.length) { if (definition?.legalDirectChildren?.length) {

View File

@ -275,7 +275,7 @@
} }
}} }}
> >
{selectedApp ? `${selectedApp?.url}` : ""} {$store.url}
{#if isPublished} {#if isPublished}
<Icon size="S" name="LinkOut" /> <Icon size="S" name="LinkOut" />
{:else} {:else}
@ -344,7 +344,12 @@
<Modal bind:this={updateAppModal} padding={false} width="600px"> <Modal bind:this={updateAppModal} padding={false} width="600px">
<UpdateAppModal <UpdateAppModal
app={selectedApp} app={{
name: $store.name,
url: $store.url,
icon: $store.icon,
appId: $store.appId,
}}
onUpdateComplete={async () => { onUpdateComplete={async () => {
await initialiseApp() await initialiseApp()
}} }}

View File

@ -16,6 +16,9 @@
export let app export let app
export let onUpdateComplete export let onUpdateComplete
$: appIdParts = app.appId ? app.appId?.split("_") : []
$: appId = appIdParts.slice(-1)[0]
const values = writable({ const values = writable({
name: app.name, name: app.name,
url: app.url, url: app.url,
@ -35,8 +38,20 @@
const setupValidation = async () => { const setupValidation = async () => {
const applications = svelteGet(apps) const applications = svelteGet(apps)
appValidation.name(validation, { apps: applications, currentApp: app }) appValidation.name(validation, {
appValidation.url(validation, { apps: applications, currentApp: app }) apps: applications,
currentApp: {
...app,
appId,
},
})
appValidation.url(validation, {
apps: applications,
currentApp: {
...app,
appId,
},
})
// init validation // init validation
const { url } = $values const { url } = $values
validation.check({ validation.check({
@ -47,7 +62,7 @@
async function updateApp() { async function updateApp() {
try { try {
await apps.update(app.instance._id, { await apps.update(app.appId, {
name: $values.name?.trim(), name: $values.name?.trim(),
url: $values.url?.trim(), url: $values.url?.trim(),
icon: { icon: {

View File

@ -52,7 +52,13 @@ export const url = (validation, { apps, currentApp } = { apps: [] }) => {
} }
return !apps return !apps
.map(app => app.url) .map(app => app.url)
.some(appUrl => appUrl?.toLowerCase() === value.toLowerCase()) .some(appUrl => {
const url =
appUrl?.[0] === "/"
? appUrl.substring(1, appUrl.length)
: appUrl
return url?.toLowerCase() === value.toLowerCase()
})
} }
) )
.test("valid-url", "Not a valid URL", value => { .test("valid-url", "Not a valid URL", value => {

View File

@ -52,6 +52,9 @@
// Build up list of illegal children from ancestors // Build up list of illegal children from ancestors
let illegalChildren = definition.illegalChildren || [] let illegalChildren = definition.illegalChildren || []
path.forEach(ancestor => { path.forEach(ancestor => {
if (ancestor._component === `@budibase/standard-components/sidepanel`) {
illegalChildren = []
}
const def = store.actions.components.getDefinition(ancestor._component) const def = store.actions.components.getDefinition(ancestor._component)
const blacklist = def?.illegalChildren?.map(x => { const blacklist = def?.illegalChildren?.map(x => {
return `@budibase/standard-components/${x}` return `@budibase/standard-components/${x}`

View File

@ -39,7 +39,7 @@
notifications.success("Copied") notifications.success("Copied")
}} }}
> >
Copy Code Copy code
</Button> </Button>
</div> </div>
{:else} {:else}

View File

@ -19,11 +19,10 @@
$: filteredApps = $apps.filter(app => app.devId == $store.appId) $: filteredApps = $apps.filter(app => app.devId == $store.appId)
$: app = filteredApps.length ? filteredApps[0] : {} $: app = filteredApps.length ? filteredApps[0] : {}
$: appUrl = `${window.origin}/app${app?.url}`
$: appDeployed = app?.status === AppStatus.DEPLOYED $: appDeployed = app?.status === AppStatus.DEPLOYED
const initialiseApp = async () => { const initialiseApp = async () => {
const applicationPkg = await API.fetchAppPackage(app.devId) const applicationPkg = await API.fetchAppPackage($store.appId)
await store.actions.initialise(applicationPkg) await store.actions.initialise(applicationPkg)
} }
</script> </script>
@ -37,7 +36,7 @@
<Layout noPadding gap="XXS"> <Layout noPadding gap="XXS">
<Label size="L">Name</Label> <Label size="L">Name</Label>
<Body>{app?.name}</Body> <Body>{$store?.name}</Body>
</Layout> </Layout>
<Layout noPadding gap="XS"> <Layout noPadding gap="XS">
@ -45,15 +44,15 @@
<div class="icon"> <div class="icon">
<Icon <Icon
size="L" size="L"
name={app?.icon?.name || "Apps"} name={$store?.icon?.name || "Apps"}
color={app?.icon?.color} color={$store?.icon?.color}
/> />
</div> </div>
</Layout> </Layout>
<Layout noPadding gap="XXS"> <Layout noPadding gap="XXS">
<Label size="L">URL</Label> <Label size="L">URL</Label>
<Body>{appUrl}</Body> <Body>{$store.url}</Body>
</Layout> </Layout>
<div> <div>
@ -75,7 +74,12 @@
<Modal bind:this={updatingModal} padding={false} width="600px"> <Modal bind:this={updatingModal} padding={false} width="600px">
<UpdateAppModal <UpdateAppModal
{app} app={{
name: $store.name,
url: $store.url,
icon: $store.icon,
appId: $store.appId,
}}
onUpdateComplete={async () => { onUpdateComplete={async () => {
await initialiseApp() await initialiseApp()
}} }}

View File

@ -1,21 +0,0 @@
import { writable, derived } from "svelte/store"
import { apps } from "./apps"
const createOverviewStore = () => {
const store = writable({
selectedAppId: null,
})
const derivedStore = derived([store, apps], ([$store, $apps]) => {
return {
...$store,
selectedApp: $apps?.find(app => app.devId === $store.selectedAppId),
}
})
return {
update: store.update,
subscribe: derivedStore.subscribe,
}
}
export const overview = createOverviewStore()

View File

@ -16,7 +16,7 @@
export let columns = null export let columns = null
const component = getContext("component") const component = getContext("component")
const { styleable, API, builderStore } = getContext("sdk") const { styleable, API, builderStore, notificationStore } = getContext("sdk")
$: columnWhitelist = columns?.map(col => col.name) $: columnWhitelist = columns?.map(col => col.name)
$: schemaOverrides = getSchemaOverrides(columns) $: schemaOverrides = getSchemaOverrides(columns)
@ -52,6 +52,8 @@
showControls={false} showControls={false}
allowExpandRows={false} allowExpandRows={false}
allowSchemaChanges={false} allowSchemaChanges={false}
notifySuccess={notificationStore.actions.success}
notifyError={notificationStore.actions.error}
/> />
</div> </div>

View File

@ -1,7 +1,7 @@
<script> <script>
import { onMount } from "svelte" import { onMount } from "svelte"
import { getContext } from "svelte" import { getContext } from "svelte"
import { Dropzone, notifications } from "@budibase/bbui" import { Dropzone } from "@budibase/bbui"
export let value export let value
export let focused = false export let focused = false
@ -11,7 +11,7 @@
export let invertX = false export let invertX = false
export let invertY = false export let invertY = false
const { API } = getContext("grid") const { API, notifications } = getContext("grid")
const imageExtensions = ["png", "tiff", "gif", "raw", "jpg", "jpeg"] const imageExtensions = ["png", "tiff", "gif", "raw", "jpg", "jpeg"]
let isOpen = false let isOpen = false
@ -40,7 +40,7 @@
} }
const handleFileTooLarge = fileSizeLimit => { const handleFileTooLarge = fileSizeLimit => {
notifications.error( $notifications.error(
`Files cannot exceed ${ `Files cannot exceed ${
fileSizeLimit / 1000000 fileSizeLimit / 1000000
}MB. Please try again with smaller files.` }MB. Please try again with smaller files.`
@ -55,7 +55,7 @@
try { try {
return await API.uploadBuilderAttachment(data) return await API.uploadBuilderAttachment(data)
} catch (error) { } catch (error) {
notifications.error("Failed to upload attachment") $notifications.error("Failed to upload attachment")
return [] return []
} }
} }

View File

@ -1,8 +1,8 @@
<script> <script>
import { Modal, ModalContent, notifications } from "@budibase/bbui" import { Modal, ModalContent } from "@budibase/bbui"
import { getContext, onMount } from "svelte" import { getContext, onMount } from "svelte"
const { selectedRows, rows, subscribe } = getContext("grid") const { selectedRows, rows, subscribe, notifications } = getContext("grid")
let modal let modal
@ -15,7 +15,7 @@
const performDeletion = async () => { const performDeletion = async () => {
const count = rowsToDelete.length const count = rowsToDelete.length
await rows.actions.deleteRows(rowsToDelete) await rows.actions.deleteRows(rowsToDelete)
notifications.success(`Deleted ${count} row${count === 1 ? "" : "s"}`) $notifications.success(`Deleted ${count} row${count === 1 ? "" : "s"}`)
} }
onMount(() => subscribe("request-bulk-delete", () => modal?.show())) onMount(() => subscribe("request-bulk-delete", () => modal?.show()))

View File

@ -44,6 +44,8 @@
export let initialSortColumn = null export let initialSortColumn = null
export let initialSortOrder = null export let initialSortOrder = null
export let initialRowHeight = null export let initialRowHeight = null
export let notifySuccess = null
export let notifyError = null
// Unique identifier for DOM nodes inside this instance // Unique identifier for DOM nodes inside this instance
const rand = Math.random() const rand = Math.random()
@ -89,6 +91,8 @@
initialSortColumn, initialSortColumn,
initialSortOrder, initialSortOrder,
initialRowHeight, initialRowHeight,
notifySuccess,
notifyError,
}) })
// Set context for children to consume // Set context for children to consume

View File

@ -78,6 +78,11 @@
} }
const startAdding = async () => { const startAdding = async () => {
// Attempt to submit if already adding a row
if (visible && !isAdding) {
await addRow()
return
}
if (visible || !firstColumn) { if (visible || !firstColumn) {
return return
} }

View File

@ -1,11 +1,5 @@
<script> <script>
import { import { clickOutside, Menu, MenuItem, Helpers } from "@budibase/bbui"
clickOutside,
Menu,
MenuItem,
Helpers,
notifications,
} from "@budibase/bbui"
import { getContext } from "svelte" import { getContext } from "svelte"
import { NewRowID } from "../lib/constants" import { NewRowID } from "../lib/constants"
@ -22,6 +16,7 @@
dispatch, dispatch,
focusedCellAPI, focusedCellAPI,
focusedRowId, focusedRowId,
notifications,
} = getContext("grid") } = getContext("grid")
$: style = makeStyle($menu) $: style = makeStyle($menu)
@ -34,7 +29,7 @@
const deleteRow = () => { const deleteRow = () => {
rows.actions.deleteRows([$focusedRow]) rows.actions.deleteRows([$focusedRow])
menu.actions.close() menu.actions.close()
notifications.success("Deleted 1 row") $notifications.success("Deleted 1 row")
} }
const duplicate = async () => { const duplicate = async () => {
@ -48,7 +43,7 @@
const copyToClipboard = async value => { const copyToClipboard = async value => {
await Helpers.copyToClipboard(value) await Helpers.copyToClipboard(value)
notifications.success("Copied to clipboard") $notifications.success("Copied to clipboard")
} }
</script> </script>

View File

@ -35,10 +35,20 @@ export const createStores = () => {
[] []
) )
// Checks if we have a certain column by name
const hasColumn = column => {
const $columns = get(columns)
const $sticky = get(stickyColumn)
return $columns.some(col => col.name === column) || $sticky?.name === column
}
return { return {
columns: { columns: {
...columns, ...columns,
subscribe: enrichedColumns.subscribe, subscribe: enrichedColumns.subscribe,
actions: {
hasColumn,
},
}, },
stickyColumn, stickyColumn,
visibleColumns, visibleColumns,
@ -121,6 +131,7 @@ export const deriveStores = context => {
columns: { columns: {
...columns, ...columns,
actions: { actions: {
...columns.actions,
saveChanges, saveChanges,
saveTable, saveTable,
changePrimaryDisplay, changePrimaryDisplay,

View File

@ -13,6 +13,8 @@ export const createStores = context => {
const initialRowHeight = getProp("initialRowHeight") const initialRowHeight = getProp("initialRowHeight")
const schemaOverrides = getProp("schemaOverrides") const schemaOverrides = getProp("schemaOverrides")
const columnWhitelist = getProp("columnWhitelist") const columnWhitelist = getProp("columnWhitelist")
const notifySuccess = getProp("notifySuccess")
const notifyError = getProp("notifyError")
return { return {
config, config,
@ -23,5 +25,7 @@ export const createStores = context => {
initialRowHeight, initialRowHeight,
schemaOverrides, schemaOverrides,
columnWhitelist, columnWhitelist,
notifySuccess,
notifyError,
} }
} }

View File

@ -14,9 +14,11 @@ import * as Clipboard from "./clipboard"
import * as Config from "./config" import * as Config from "./config"
import * as Sort from "./sort" import * as Sort from "./sort"
import * as Filter from "./filter" import * as Filter from "./filter"
import * as Notifications from "./notifications"
const DependencyOrderedStores = [ const DependencyOrderedStores = [
Config, Config,
Notifications,
Sort, Sort,
Filter, Filter,
Bounds, Bounds,

View File

@ -0,0 +1,23 @@
import { notifications as BBUINotifications } from "@budibase/bbui"
import { derived } from "svelte/store"
export const createStores = context => {
const { notifySuccess, notifyError } = context
// Normally we would not derive a store in "createStores" as it should be
// dependency free, but in this case it's safe as we only depend on grid props
// which are guaranteed to be first in the dependency chain
const notifications = derived(
[notifySuccess, notifyError],
([$notifySuccess, $notifyError]) => {
return {
success: $notifySuccess || BBUINotifications.success,
error: $notifyError || BBUINotifications.error,
}
}
)
return {
notifications,
}
}

View File

@ -1,6 +1,5 @@
import { writable, derived, get } from "svelte/store" import { writable, derived, get } from "svelte/store"
import { fetchData } from "../../../fetch/fetchData" import { fetchData } from "../../../fetch/fetchData"
import { notifications } from "@budibase/bbui"
import { NewRowID, RowPageSize } from "../lib/constants" import { NewRowID, RowPageSize } from "../lib/constants"
import { tick } from "svelte" import { tick } from "svelte"
@ -71,6 +70,7 @@ export const deriveStores = context => {
previousFocusedRowId, previousFocusedRowId,
hasNextPage, hasNextPage,
error, error,
notifications,
} = context } = context
const instanceLoaded = writable(false) const instanceLoaded = writable(false)
const fetch = writable(null) const fetch = writable(null)
@ -203,10 +203,23 @@ export const deriveStores = context => {
// state, storing error messages against relevant cells // state, storing error messages against relevant cells
const handleValidationError = (rowId, error) => { const handleValidationError = (rowId, error) => {
if (error?.json?.validationErrors) { if (error?.json?.validationErrors) {
// Normal validation error // Normal validation errors
const keys = Object.keys(error.json.validationErrors) const keys = Object.keys(error.json.validationErrors)
const $columns = get(columns) const $columns = get(columns)
// Filter out missing columns from columns that we have
let erroredColumns = []
let missingColumns = []
for (let column of keys) { for (let column of keys) {
if (columns.actions.hasColumn(column)) {
erroredColumns.push(column)
} else {
missingColumns.push(column)
}
}
// Process errors for columns that we have
for (let column of erroredColumns) {
validation.actions.setError( validation.actions.setError(
`${rowId}-${column}`, `${rowId}-${column}`,
`${column} ${error.json.validationErrors[column]}` `${column} ${error.json.validationErrors[column]}`
@ -221,8 +234,16 @@ export const deriveStores = context => {
}) })
} }
} }
// Notify about missing columns
for (let column of missingColumns) {
get(notifications).error(`${column} is required but is missing`)
}
// Focus the first cell with an error // Focus the first cell with an error
focusedCellId.set(`${rowId}-${keys[0]}`) if (erroredColumns.length) {
focusedCellId.set(`${rowId}-${erroredColumns[0]}`)
}
} else { } else {
// Some other error - just update the current cell // Some other error - just update the current cell
validation.actions.setError(get(focusedCellId), error?.message || "Error") validation.actions.setError(get(focusedCellId), error?.message || "Error")
@ -250,7 +271,7 @@ export const deriveStores = context => {
} }
// Refresh row to ensure data is in the correct format // Refresh row to ensure data is in the correct format
notifications.success("Row created successfully") get(notifications).success("Row created successfully")
return newRow return newRow
} catch (error) { } catch (error) {
if (bubble) { if (bubble) {