Merge branch 'master' of github.com:budibase/budibase into dd-trace-5.43.0
This commit is contained in:
commit
c769d5860c
|
@ -27,5 +27,6 @@
|
|||
},
|
||||
"[handlebars]": {
|
||||
"editor.formatOnSave": false
|
||||
}
|
||||
},
|
||||
"eslint.validate": ["javascript", "typescript", "svelte"]
|
||||
}
|
||||
|
|
|
@ -10,7 +10,7 @@
|
|||
},
|
||||
"dependencies": {
|
||||
"bulma": "^0.9.3",
|
||||
"next": "14.2.21",
|
||||
"next": "14.2.25",
|
||||
"node-fetch": "^3.2.10",
|
||||
"sass": "^1.52.3",
|
||||
"react": "17.0.2",
|
||||
|
|
|
@ -46,10 +46,10 @@
|
|||
resolved "https://registry.yarnpkg.com/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz#b520529ec21d8e5945a1851dfd1c32e94e39ff45"
|
||||
integrity sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==
|
||||
|
||||
"@next/env@14.2.21":
|
||||
version "14.2.21"
|
||||
resolved "https://registry.yarnpkg.com/@next/env/-/env-14.2.21.tgz#09ff0813d29c596397e141205d4f5fd5c236bdd0"
|
||||
integrity sha512-lXcwcJd5oR01tggjWJ6SrNNYFGuOOMB9c251wUNkjCpkoXOPkDeF/15c3mnVlBqrW4JJXb2kVxDFhC4GduJt2A==
|
||||
"@next/env@14.2.25":
|
||||
version "14.2.25"
|
||||
resolved "https://registry.yarnpkg.com/@next/env/-/env-14.2.25.tgz#936d10b967e103e49a4bcea1e97292d5605278dd"
|
||||
integrity sha512-JnzQ2cExDeG7FxJwqAksZ3aqVJrHjFwZQAEJ9gQZSoEhIow7SNoKZzju/AwQ+PLIR4NY8V0rhcVozx/2izDO0w==
|
||||
|
||||
"@next/eslint-plugin-next@12.1.0":
|
||||
version "12.1.0"
|
||||
|
@ -58,50 +58,50 @@
|
|||
dependencies:
|
||||
glob "7.1.7"
|
||||
|
||||
"@next/swc-darwin-arm64@14.2.21":
|
||||
version "14.2.21"
|
||||
resolved "https://registry.yarnpkg.com/@next/swc-darwin-arm64/-/swc-darwin-arm64-14.2.21.tgz#32a31992aace1440981df9cf7cb3af7845d94fec"
|
||||
integrity sha512-HwEjcKsXtvszXz5q5Z7wCtrHeTTDSTgAbocz45PHMUjU3fBYInfvhR+ZhavDRUYLonm53aHZbB09QtJVJj8T7g==
|
||||
"@next/swc-darwin-arm64@14.2.25":
|
||||
version "14.2.25"
|
||||
resolved "https://registry.yarnpkg.com/@next/swc-darwin-arm64/-/swc-darwin-arm64-14.2.25.tgz#7bcccfda0c0ff045c45fbe34c491b7368e373e3d"
|
||||
integrity sha512-09clWInF1YRd6le00vt750s3m7SEYNehz9C4PUcSu3bAdCTpjIV4aTYQZ25Ehrr83VR1rZeqtKUPWSI7GfuKZQ==
|
||||
|
||||
"@next/swc-darwin-x64@14.2.21":
|
||||
version "14.2.21"
|
||||
resolved "https://registry.yarnpkg.com/@next/swc-darwin-x64/-/swc-darwin-x64-14.2.21.tgz#5ab4b3f6685b6b52f810d0f5cf6e471480ddffdb"
|
||||
integrity sha512-TSAA2ROgNzm4FhKbTbyJOBrsREOMVdDIltZ6aZiKvCi/v0UwFmwigBGeqXDA97TFMpR3LNNpw52CbVelkoQBxA==
|
||||
"@next/swc-darwin-x64@14.2.25":
|
||||
version "14.2.25"
|
||||
resolved "https://registry.yarnpkg.com/@next/swc-darwin-x64/-/swc-darwin-x64-14.2.25.tgz#b489e209d7b405260b73f69a38186ed150fb7a08"
|
||||
integrity sha512-V+iYM/QR+aYeJl3/FWWU/7Ix4b07ovsQ5IbkwgUK29pTHmq+5UxeDr7/dphvtXEq5pLB/PucfcBNh9KZ8vWbug==
|
||||
|
||||
"@next/swc-linux-arm64-gnu@14.2.21":
|
||||
version "14.2.21"
|
||||
resolved "https://registry.yarnpkg.com/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-14.2.21.tgz#8a0e1fa887aef19ca218af2af515d0a5ee67ba3f"
|
||||
integrity sha512-0Dqjn0pEUz3JG+AImpnMMW/m8hRtl1GQCNbO66V1yp6RswSTiKmnHf3pTX6xMdJYSemf3O4Q9ykiL0jymu0TuA==
|
||||
"@next/swc-linux-arm64-gnu@14.2.25":
|
||||
version "14.2.25"
|
||||
resolved "https://registry.yarnpkg.com/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-14.2.25.tgz#ba064fabfdce0190d9859493d8232fffa84ef2e2"
|
||||
integrity sha512-LFnV2899PJZAIEHQ4IMmZIgL0FBieh5keMnriMY1cK7ompR+JUd24xeTtKkcaw8QmxmEdhoE5Mu9dPSuDBgtTg==
|
||||
|
||||
"@next/swc-linux-arm64-musl@14.2.21":
|
||||
version "14.2.21"
|
||||
resolved "https://registry.yarnpkg.com/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-14.2.21.tgz#ddad844406b42fa8965fe11250abc85c1fe0fd05"
|
||||
integrity sha512-Ggfw5qnMXldscVntwnjfaQs5GbBbjioV4B4loP+bjqNEb42fzZlAaK+ldL0jm2CTJga9LynBMhekNfV8W4+HBw==
|
||||
"@next/swc-linux-arm64-musl@14.2.25":
|
||||
version "14.2.25"
|
||||
resolved "https://registry.yarnpkg.com/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-14.2.25.tgz#bf0018267e4e0fbfa1524750321f8cae855144a3"
|
||||
integrity sha512-QC5y5PPTmtqFExcKWKYgUNkHeHE/z3lUsu83di488nyP0ZzQ3Yse2G6TCxz6nNsQwgAx1BehAJTZez+UQxzLfw==
|
||||
|
||||
"@next/swc-linux-x64-gnu@14.2.21":
|
||||
version "14.2.21"
|
||||
resolved "https://registry.yarnpkg.com/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-14.2.21.tgz#db55fd666f9ba27718f65caa54b622a912cdd16b"
|
||||
integrity sha512-uokj0lubN1WoSa5KKdThVPRffGyiWlm/vCc/cMkWOQHw69Qt0X1o3b2PyLLx8ANqlefILZh1EdfLRz9gVpG6tg==
|
||||
"@next/swc-linux-x64-gnu@14.2.25":
|
||||
version "14.2.25"
|
||||
resolved "https://registry.yarnpkg.com/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-14.2.25.tgz#64f5a6016a7148297ee80542e0fd788418a32472"
|
||||
integrity sha512-y6/ML4b9eQ2D/56wqatTJN5/JR8/xdObU2Fb1RBidnrr450HLCKr6IJZbPqbv7NXmje61UyxjF5kvSajvjye5w==
|
||||
|
||||
"@next/swc-linux-x64-musl@14.2.21":
|
||||
version "14.2.21"
|
||||
resolved "https://registry.yarnpkg.com/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-14.2.21.tgz#dddb850353624efcd58c4c4e30ad8a1aab379642"
|
||||
integrity sha512-iAEBPzWNbciah4+0yI4s7Pce6BIoxTQ0AGCkxn/UBuzJFkYyJt71MadYQkjPqCQCJAFQ26sYh7MOKdU+VQFgPg==
|
||||
"@next/swc-linux-x64-musl@14.2.25":
|
||||
version "14.2.25"
|
||||
resolved "https://registry.yarnpkg.com/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-14.2.25.tgz#58dc636d7c55828478159546f7b95ab1e902301c"
|
||||
integrity sha512-sPX0TSXHGUOZFvv96GoBXpB3w4emMqKeMgemrSxI7A6l55VBJp/RKYLwZIB9JxSqYPApqiREaIIap+wWq0RU8w==
|
||||
|
||||
"@next/swc-win32-arm64-msvc@14.2.21":
|
||||
version "14.2.21"
|
||||
resolved "https://registry.yarnpkg.com/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-14.2.21.tgz#290012ee57b196d3d2d04853e6bf0179cae9fbaf"
|
||||
integrity sha512-plykgB3vL2hB4Z32W3ktsfqyuyGAPxqwiyrAi2Mr8LlEUhNn9VgkiAl5hODSBpzIfWweX3er1f5uNpGDygfQVQ==
|
||||
"@next/swc-win32-arm64-msvc@14.2.25":
|
||||
version "14.2.25"
|
||||
resolved "https://registry.yarnpkg.com/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-14.2.25.tgz#93562d447c799bded1e89c1a62d5195a2a8c6c0d"
|
||||
integrity sha512-ReO9S5hkA1DU2cFCsGoOEp7WJkhFzNbU/3VUF6XxNGUCQChyug6hZdYL/istQgfT/GWE6PNIg9cm784OI4ddxQ==
|
||||
|
||||
"@next/swc-win32-ia32-msvc@14.2.21":
|
||||
version "14.2.21"
|
||||
resolved "https://registry.yarnpkg.com/@next/swc-win32-ia32-msvc/-/swc-win32-ia32-msvc-14.2.21.tgz#c959135a78cab18cca588d11d1e33bcf199590d4"
|
||||
integrity sha512-w5bacz4Vxqrh06BjWgua3Yf7EMDb8iMcVhNrNx8KnJXt8t+Uu0Zg4JHLDL/T7DkTCEEfKXO/Er1fcfWxn2xfPA==
|
||||
"@next/swc-win32-ia32-msvc@14.2.25":
|
||||
version "14.2.25"
|
||||
resolved "https://registry.yarnpkg.com/@next/swc-win32-ia32-msvc/-/swc-win32-ia32-msvc-14.2.25.tgz#ad85a33466be1f41d083211ea21adc0d2c6e6554"
|
||||
integrity sha512-DZ/gc0o9neuCDyD5IumyTGHVun2dCox5TfPQI/BJTYwpSNYM3CZDI4i6TOdjeq1JMo+Ug4kPSMuZdwsycwFbAw==
|
||||
|
||||
"@next/swc-win32-x64-msvc@14.2.21":
|
||||
version "14.2.21"
|
||||
resolved "https://registry.yarnpkg.com/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-14.2.21.tgz#21ff892286555b90538a7d1b505ea21a005d6ead"
|
||||
integrity sha512-sT6+llIkzpsexGYZq8cjjthRyRGe5cJVhqh12FmlbxHqna6zsDDK8UNaV7g41T6atFHCJUPeLb3uyAwrBwy0NA==
|
||||
"@next/swc-win32-x64-msvc@14.2.25":
|
||||
version "14.2.25"
|
||||
resolved "https://registry.yarnpkg.com/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-14.2.25.tgz#3969c66609e683ec63a6a9f320a855f7be686a08"
|
||||
integrity sha512-KSznmS6eFjQ9RJ1nEc66kJvtGIL1iZMYmGEXsZPh2YtnLtqrgdVvKXJY2ScjjoFnG6nGLyPFR0UiEvDwVah4Tw==
|
||||
|
||||
"@nodelib/fs.scandir@2.1.5":
|
||||
version "2.1.5"
|
||||
|
@ -1253,12 +1253,12 @@ natural-compare@^1.4.0:
|
|||
resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7"
|
||||
integrity sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=
|
||||
|
||||
next@14.2.21:
|
||||
version "14.2.21"
|
||||
resolved "https://registry.yarnpkg.com/next/-/next-14.2.21.tgz#f6da9e2abba1a0e4ca7a5273825daf06632554ba"
|
||||
integrity sha512-rZmLwucLHr3/zfDMYbJXbw0ZeoBpirxkXuvsJbk7UPorvPYZhP7vq7aHbKnU7dQNCYIimRrbB2pp3xmf+wsYUg==
|
||||
next@14.2.25:
|
||||
version "14.2.25"
|
||||
resolved "https://registry.yarnpkg.com/next/-/next-14.2.25.tgz#0657551fde6a97f697cf9870e9ccbdaa465c6008"
|
||||
integrity sha512-N5M7xMc4wSb4IkPvEV5X2BRRXUmhVHNyaXwEM86+voXthSZz8ZiRyQW4p9mwAoAPIm6OzuVZtn7idgEJeAJN3Q==
|
||||
dependencies:
|
||||
"@next/env" "14.2.21"
|
||||
"@next/env" "14.2.25"
|
||||
"@swc/helpers" "0.5.5"
|
||||
busboy "1.6.0"
|
||||
caniuse-lite "^1.0.30001579"
|
||||
|
@ -1266,15 +1266,15 @@ next@14.2.21:
|
|||
postcss "8.4.31"
|
||||
styled-jsx "5.1.1"
|
||||
optionalDependencies:
|
||||
"@next/swc-darwin-arm64" "14.2.21"
|
||||
"@next/swc-darwin-x64" "14.2.21"
|
||||
"@next/swc-linux-arm64-gnu" "14.2.21"
|
||||
"@next/swc-linux-arm64-musl" "14.2.21"
|
||||
"@next/swc-linux-x64-gnu" "14.2.21"
|
||||
"@next/swc-linux-x64-musl" "14.2.21"
|
||||
"@next/swc-win32-arm64-msvc" "14.2.21"
|
||||
"@next/swc-win32-ia32-msvc" "14.2.21"
|
||||
"@next/swc-win32-x64-msvc" "14.2.21"
|
||||
"@next/swc-darwin-arm64" "14.2.25"
|
||||
"@next/swc-darwin-x64" "14.2.25"
|
||||
"@next/swc-linux-arm64-gnu" "14.2.25"
|
||||
"@next/swc-linux-arm64-musl" "14.2.25"
|
||||
"@next/swc-linux-x64-gnu" "14.2.25"
|
||||
"@next/swc-linux-x64-musl" "14.2.25"
|
||||
"@next/swc-win32-arm64-msvc" "14.2.25"
|
||||
"@next/swc-win32-ia32-msvc" "14.2.25"
|
||||
"@next/swc-win32-x64-msvc" "14.2.25"
|
||||
|
||||
node-domexception@^1.0.0:
|
||||
version "1.0.0"
|
||||
|
|
|
@ -1,25 +1,26 @@
|
|||
<script>
|
||||
<script lang="ts">
|
||||
import "@spectrum-css/textfield/dist/index-vars.css"
|
||||
import { createEventDispatcher, onMount } from "svelte"
|
||||
import clickOutside from "../../Actions/click_outside"
|
||||
import Divider from "../../Divider/Divider.svelte"
|
||||
import type { EnvDropdownType } from "../../types"
|
||||
|
||||
export let value = null
|
||||
export let placeholder = null
|
||||
export let type = "text"
|
||||
export let disabled = false
|
||||
export let id = null
|
||||
export let readonly = false
|
||||
export let updateOnChange = true
|
||||
export let align
|
||||
export let autofocus = false
|
||||
export let value: string | number | undefined = undefined
|
||||
export let placeholder: string | undefined = undefined
|
||||
export let type: EnvDropdownType = "text"
|
||||
export let disabled: boolean = false
|
||||
export let id: string | undefined = undefined
|
||||
export let readonly: boolean = false
|
||||
export let updateOnChange: boolean = true
|
||||
export let align: string | undefined = undefined
|
||||
export let autofocus: boolean = false
|
||||
export let variables
|
||||
export let showModal
|
||||
export let showModal: () => void
|
||||
export let environmentVariablesEnabled
|
||||
export let handleUpgradePanel
|
||||
export let handleUpgradePanel: () => void
|
||||
const dispatch = createEventDispatcher()
|
||||
|
||||
let field
|
||||
let field: HTMLInputElement
|
||||
let focus = false
|
||||
let iconFocused = false
|
||||
let open = false
|
||||
|
@ -30,7 +31,7 @@
|
|||
// Strips the name out of the value which is {{ env.Variable }} resulting in an array like ["Variable"]
|
||||
$: hbsValue = String(value)?.match(STRIP_NAME_REGEX) || []
|
||||
|
||||
const updateValue = newValue => {
|
||||
const updateValue = (newValue: any) => {
|
||||
if (readonly) {
|
||||
return
|
||||
}
|
||||
|
@ -48,7 +49,7 @@
|
|||
focus = true
|
||||
}
|
||||
|
||||
const onBlur = event => {
|
||||
const onBlur = (event: any) => {
|
||||
if (readonly) {
|
||||
return
|
||||
}
|
||||
|
@ -56,14 +57,14 @@
|
|||
updateValue(event.target.value)
|
||||
}
|
||||
|
||||
const onInput = event => {
|
||||
const onInput = (event: any) => {
|
||||
if (readonly || !updateOnChange) {
|
||||
return
|
||||
}
|
||||
updateValue(event.target.value)
|
||||
}
|
||||
|
||||
const handleOutsideClick = event => {
|
||||
const handleOutsideClick = (event: Event) => {
|
||||
if (open) {
|
||||
event.stopPropagation()
|
||||
open = false
|
||||
|
@ -73,7 +74,7 @@
|
|||
}
|
||||
}
|
||||
|
||||
const handleVarSelect = variable => {
|
||||
const handleVarSelect = (variable: string) => {
|
||||
open = false
|
||||
focus = false
|
||||
iconFocused = false
|
||||
|
@ -121,10 +122,10 @@
|
|||
|
||||
<input
|
||||
bind:this={field}
|
||||
disabled={hbsValue.length || disabled}
|
||||
disabled={!!hbsValue.length || disabled}
|
||||
{readonly}
|
||||
{id}
|
||||
value={hbsValue.length ? `{{ ${hbsValue[0]} }}` : value}
|
||||
value={(hbsValue.length ? `{{ ${hbsValue[0]} }}` : value) ?? ""}
|
||||
placeholder={placeholder || ""}
|
||||
on:click
|
||||
on:blur
|
||||
|
|
|
@ -13,7 +13,7 @@
|
|||
export let quiet = false
|
||||
export let align: "left" | "right" | "center" | undefined = undefined
|
||||
export let autofocus: boolean | null = false
|
||||
export let autocomplete: boolean | undefined
|
||||
export let autocomplete: boolean | string | undefined
|
||||
|
||||
const dispatch = createEventDispatcher()
|
||||
|
||||
|
|
|
@ -1,26 +1,26 @@
|
|||
<script>
|
||||
<script lang="ts">
|
||||
import Field from "./Field.svelte"
|
||||
import EnvDropdown from "./Core/EnvDropdown.svelte"
|
||||
import { createEventDispatcher } from "svelte"
|
||||
import type { EnvDropdownType } from "../types"
|
||||
|
||||
export let value = null
|
||||
export let label = null
|
||||
export let labelPosition = "above"
|
||||
export let placeholder = null
|
||||
export let type = "text"
|
||||
export let value: string | undefined = undefined
|
||||
export let label: string | undefined = undefined
|
||||
export let labelPosition: string = "above"
|
||||
export let placeholder: string | undefined = undefined
|
||||
export let type: EnvDropdownType = "text"
|
||||
export let disabled = false
|
||||
export let readonly = false
|
||||
export let error = null
|
||||
export let error: string | undefined = undefined
|
||||
export let updateOnChange = true
|
||||
export let quiet = false
|
||||
export let autofocus
|
||||
export let variables
|
||||
export let showModal
|
||||
export let helpText = null
|
||||
export let environmentVariablesEnabled
|
||||
export let handleUpgradePanel
|
||||
export let autofocus: boolean = false
|
||||
export let variables: { name: string }[] = []
|
||||
export let showModal: () => void
|
||||
export let helpText: string | undefined = undefined
|
||||
export let environmentVariablesEnabled: boolean = false
|
||||
export let handleUpgradePanel: () => void = () => {}
|
||||
const dispatch = createEventDispatcher()
|
||||
const onChange = e => {
|
||||
const onChange = (e: any) => {
|
||||
value = e.detail
|
||||
dispatch("change", e.detail)
|
||||
}
|
||||
|
@ -29,13 +29,11 @@
|
|||
<Field {helpText} {label} {labelPosition} {error}>
|
||||
<EnvDropdown
|
||||
{updateOnChange}
|
||||
{error}
|
||||
{disabled}
|
||||
{readonly}
|
||||
{value}
|
||||
{placeholder}
|
||||
{type}
|
||||
{quiet}
|
||||
{autofocus}
|
||||
{variables}
|
||||
{showModal}
|
||||
|
|
|
@ -14,7 +14,7 @@
|
|||
export let updateOnChange = true
|
||||
export let quiet = false
|
||||
export let autofocus: boolean | undefined = undefined
|
||||
export let autocomplete: boolean | undefined = undefined
|
||||
export let autocomplete: boolean | string | undefined = undefined
|
||||
export let helpText: string | undefined = undefined
|
||||
|
||||
const dispatch = createEventDispatcher()
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
import Icon from "../Icon/Icon.svelte"
|
||||
|
||||
const dispatch = createEventDispatcher()
|
||||
const actionMenu = getContext("actionMenu") as { hideAll: () => void }
|
||||
const actionMenu = getContext("actionMenu")
|
||||
|
||||
export let icon: string | undefined = undefined
|
||||
export let disabled: boolean | undefined = undefined
|
||||
|
|
|
@ -1,7 +1,9 @@
|
|||
import { ActionMenu } from "./types"
|
||||
import { ModalContext } from "./types"
|
||||
|
||||
declare module "svelte" {
|
||||
export function getContext(key: "actionMenu"): ActionMenu | undefined
|
||||
export function getContext(key: "bbui-modal"): ModalContext
|
||||
}
|
||||
|
||||
export const Modal = "bbui-modal"
|
||||
|
|
|
@ -111,3 +111,5 @@ export { banner, BANNER_TYPES } from "./Stores/banner"
|
|||
|
||||
// Helpers
|
||||
export * as Helpers from "./helpers"
|
||||
|
||||
export type * from "./types"
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
export interface ActionMenu {
|
||||
hide: () => void
|
||||
hideAll: () => void
|
||||
}
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
export type EnvDropdownType = "text" | "number" | "password" | "port"
|
|
@ -0,0 +1,3 @@
|
|||
export * from "./actionMenu"
|
||||
export * from "./envDropdown"
|
||||
export * from "./modalContext"
|
|
@ -0,0 +1,3 @@
|
|||
export interface ModalContext {
|
||||
hide: () => void
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
<script>
|
||||
<script lang="ts">
|
||||
import ObjectField from "./fields/Object.svelte"
|
||||
import BooleanField from "./fields/Boolean.svelte"
|
||||
import LongFormField from "./fields/LongForm.svelte"
|
||||
|
@ -6,15 +6,22 @@
|
|||
import StringField from "./fields/String.svelte"
|
||||
import SelectField from "./fields/Select.svelte"
|
||||
|
||||
export let type
|
||||
export let value
|
||||
export let error
|
||||
export let name
|
||||
export let config
|
||||
export let showModal = () => {}
|
||||
export let placeholder
|
||||
type InputType =
|
||||
| "string"
|
||||
| "boolean"
|
||||
| "object"
|
||||
| "longForm"
|
||||
| "fieldGroup"
|
||||
| "select"
|
||||
|
||||
const selectComponent = type => {
|
||||
export let type: InputType
|
||||
export let value: any
|
||||
export let error: string | null
|
||||
export let name: string
|
||||
export let config: any = undefined
|
||||
export let placeholder: string | undefined = undefined
|
||||
|
||||
const selectComponent = (type: InputType) => {
|
||||
if (type === "object") {
|
||||
return ObjectField
|
||||
} else if (type === "boolean") {
|
||||
|
@ -40,7 +47,6 @@
|
|||
{error}
|
||||
{name}
|
||||
{config}
|
||||
{showModal}
|
||||
{placeholder}
|
||||
on:blur
|
||||
on:change
|
||||
|
|
|
@ -1,33 +1,23 @@
|
|||
<script>
|
||||
import { Label, EnvDropdown } from "@budibase/bbui"
|
||||
import { environment, licensing } from "@/stores/portal"
|
||||
<script lang="ts">
|
||||
import { Label } from "@budibase/bbui"
|
||||
import EnvVariableInput from "@/components/portal/environment/EnvVariableInput.svelte"
|
||||
|
||||
export let type
|
||||
export let name
|
||||
export let value
|
||||
export let error
|
||||
export let placeholder
|
||||
export let showModal = () => {}
|
||||
|
||||
async function handleUpgradePanel() {
|
||||
await environment.upgradePanelOpened()
|
||||
$licensing.goToUpgradePage()
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="form-row">
|
||||
<Label>{name}</Label>
|
||||
<EnvDropdown
|
||||
<EnvVariableInput
|
||||
on:change
|
||||
on:blur
|
||||
type={type === "port" ? "string" : type}
|
||||
{value}
|
||||
{error}
|
||||
{placeholder}
|
||||
variables={$environment.variables}
|
||||
environmentVariablesEnabled={$licensing.environmentVariablesEnabled}
|
||||
{showModal}
|
||||
{handleUpgradePanel}
|
||||
/>
|
||||
</div>
|
||||
|
||||
|
|
|
@ -1,19 +1,10 @@
|
|||
<script>
|
||||
import {
|
||||
keepOpen,
|
||||
Modal,
|
||||
notifications,
|
||||
Body,
|
||||
Layout,
|
||||
ModalContent,
|
||||
} from "@budibase/bbui"
|
||||
import { keepOpen, Body, Layout, ModalContent } from "@budibase/bbui"
|
||||
import { processStringSync } from "@budibase/string-templates"
|
||||
import CreateEditVariableModal from "@/components/portal/environment/CreateEditVariableModal.svelte"
|
||||
import ConfigInput from "./ConfigInput.svelte"
|
||||
import { createValidatedConfigStore } from "./stores/validatedConfig"
|
||||
import { createValidatedNameStore } from "./stores/validatedName"
|
||||
import { get } from "svelte/store"
|
||||
import { environment } from "@/stores/portal"
|
||||
|
||||
export let integration
|
||||
export let config
|
||||
|
@ -39,24 +30,6 @@
|
|||
|
||||
return keepOpen
|
||||
}
|
||||
|
||||
let createVariableModal
|
||||
let configValueSetterCallback = () => {}
|
||||
|
||||
const showModal = setter => {
|
||||
configValueSetterCallback = setter
|
||||
createVariableModal.show()
|
||||
}
|
||||
|
||||
async function saveVariable(data) {
|
||||
try {
|
||||
await environment.createVariable(data)
|
||||
configValueSetterCallback(`{{ env.${data.name} }}`)
|
||||
createVariableModal.hide()
|
||||
} catch (err) {
|
||||
notifications.error(`Failed to create variable: ${err.message}`)
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<ModalContent
|
||||
|
@ -79,7 +52,6 @@
|
|||
value={$nameStore.name}
|
||||
error={$nameStore.error}
|
||||
name="Name"
|
||||
showModal={() => showModal(nameStore.updateValue)}
|
||||
on:blur={nameStore.markActive}
|
||||
on:change={e => nameStore.updateValue(e.detail)}
|
||||
/>
|
||||
|
@ -94,15 +66,9 @@
|
|||
{name}
|
||||
{config}
|
||||
{placeholder}
|
||||
showModal={() =>
|
||||
showModal(newValue => configStore.updateFieldValue(key, newValue))}
|
||||
on:blur={() => configStore.markFieldActive(key)}
|
||||
on:change={e => configStore.updateFieldValue(key, e.detail)}
|
||||
/>
|
||||
{/if}
|
||||
{/each}
|
||||
</ModalContent>
|
||||
|
||||
<Modal bind:this={createVariableModal}>
|
||||
<CreateEditVariableModal save={saveVariable} />
|
||||
</Modal>
|
||||
|
|
|
@ -9,10 +9,10 @@
|
|||
|
||||
export let value = ""
|
||||
export let bindings = []
|
||||
export let placeholder
|
||||
export let placeholder = undefined
|
||||
export let label
|
||||
export let disabled = false
|
||||
export let options
|
||||
export let options = undefined
|
||||
export let appendBindingsAsOptions = true
|
||||
export let error
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
<script>
|
||||
import { beforeUrlChange, goto, params } from "@roxi/routify"
|
||||
import { datasources, flags, integrations, queries } from "@/stores/builder"
|
||||
import { environment } from "@/stores/portal"
|
||||
|
||||
import {
|
||||
Banner,
|
||||
Body,
|
||||
|
@ -407,13 +407,6 @@
|
|||
notifications.error("Error getting datasources")
|
||||
}
|
||||
|
||||
try {
|
||||
// load the environment variables
|
||||
await environment.loadVariables()
|
||||
} catch (error) {
|
||||
notifications.error(`Error getting environment variables - ${error}`)
|
||||
}
|
||||
|
||||
datasource = $datasources.list.find(ds => ds._id === query?.datasourceId)
|
||||
const datasourceUrl = datasource?.config.url
|
||||
const qs = query?.fields.queryString
|
||||
|
|
|
@ -65,7 +65,7 @@
|
|||
</script>
|
||||
|
||||
<DetailPopover bind:this={popover} {title} align={PopoverAlignment.Right}>
|
||||
<div slot="anchor" class:display-new={!authConfig}>
|
||||
<div slot="anchor" class:display-new={!authConfig && oauth2Enabled}>
|
||||
<ActionButton icon="LockClosed" quiet selected>
|
||||
{#if !authConfig}
|
||||
Authentication
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
<script>
|
||||
<script lang="ts">
|
||||
import {
|
||||
ModalContent,
|
||||
Button,
|
||||
|
@ -14,23 +14,23 @@
|
|||
|
||||
const modalContext = getContext(Context.Modal)
|
||||
|
||||
export let save
|
||||
export let row
|
||||
export let save: any
|
||||
export let row: { name: string } | undefined = undefined
|
||||
|
||||
let deleteDialog
|
||||
let deleteDialog: ConfirmDialog
|
||||
let name = row?.name || ""
|
||||
let productionValue
|
||||
let developmentValue
|
||||
let productionValue: string
|
||||
let developmentValue: string
|
||||
let useProductionValue = true
|
||||
|
||||
const HasSpacesRegex = /[\\"\s]/
|
||||
|
||||
const deleteVariable = async name => {
|
||||
const deleteVariable = async (name: string) => {
|
||||
try {
|
||||
await environment.deleteVariable(name)
|
||||
modalContext.hide()
|
||||
notifications.success("Environment variable deleted")
|
||||
} catch (err) {
|
||||
} catch (err: any) {
|
||||
notifications.error(err.message)
|
||||
}
|
||||
}
|
||||
|
@ -43,7 +43,7 @@
|
|||
development: developmentValue,
|
||||
})
|
||||
notifications.success("Environment variable saved")
|
||||
} catch (err) {
|
||||
} catch (err: any) {
|
||||
notifications.error(`Error saving environment variable - ${err.message}`)
|
||||
}
|
||||
}
|
||||
|
@ -55,10 +55,10 @@
|
|||
title={!row ? "Add new environment variable" : "Edit environment variable"}
|
||||
>
|
||||
<Input
|
||||
disabled={row}
|
||||
disabled={!!row}
|
||||
label="Name"
|
||||
bind:value={name}
|
||||
error={HasSpacesRegex.test(name) && "Must not include spaces"}
|
||||
error={HasSpacesRegex.test(name) ? "Must not include spaces" : undefined}
|
||||
/>
|
||||
<div>
|
||||
<Heading size="XS">Production</Heading>
|
||||
|
@ -100,12 +100,12 @@
|
|||
<ConfirmDialog
|
||||
bind:this={deleteDialog}
|
||||
onOk={() => {
|
||||
deleteVariable(row.name)
|
||||
deleteVariable(name)
|
||||
}}
|
||||
okText="Delete Environment Variable"
|
||||
title="Confirm Deletion"
|
||||
>
|
||||
Are you sure you wish to delete the environment variable
|
||||
<i>{row.name}?</i>
|
||||
<i>{name}?</i>
|
||||
This action cannot be undone.
|
||||
</ConfirmDialog>
|
||||
|
|
|
@ -0,0 +1,58 @@
|
|||
<script lang="ts">
|
||||
import {
|
||||
EnvDropdown,
|
||||
Modal,
|
||||
notifications,
|
||||
type EnvDropdownType,
|
||||
} from "@budibase/bbui"
|
||||
import { environment, licensing } from "@/stores/portal"
|
||||
import CreateEditVariableModal from "./CreateEditVariableModal.svelte"
|
||||
import type { CreateEnvironmentVariableRequest } from "@budibase/types"
|
||||
import { onMount } from "svelte"
|
||||
|
||||
export let label: string = ""
|
||||
export let type: EnvDropdownType = "text"
|
||||
export let value: string | undefined = undefined
|
||||
export let error: string | undefined = undefined
|
||||
export let placeholder: string | undefined = undefined
|
||||
|
||||
let modal: Modal
|
||||
|
||||
async function handleUpgradePanel() {
|
||||
await environment.upgradePanelOpened()
|
||||
$licensing.goToUpgradePage()
|
||||
}
|
||||
|
||||
async function saveVariable(data: CreateEnvironmentVariableRequest) {
|
||||
await environment.createVariable(data)
|
||||
value = `{{ env.${data.name} }}`
|
||||
modal.hide()
|
||||
}
|
||||
|
||||
onMount(async () => {
|
||||
try {
|
||||
// load the environment variables
|
||||
await environment.loadVariables()
|
||||
} catch (error) {
|
||||
notifications.error(`Error getting environment variables - ${error}`)
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<EnvDropdown
|
||||
on:change
|
||||
on:blur
|
||||
bind:value
|
||||
{label}
|
||||
type={type === "port" ? "text" : type}
|
||||
{error}
|
||||
{placeholder}
|
||||
variables={$environment.variables}
|
||||
environmentVariablesEnabled={$licensing.environmentVariablesEnabled}
|
||||
showModal={() => modal.show()}
|
||||
{handleUpgradePanel}
|
||||
/>
|
||||
|
||||
<Modal bind:this={modal}>
|
||||
<CreateEditVariableModal save={saveVariable} />
|
||||
</Modal>
|
|
@ -1,4 +1,4 @@
|
|||
<script>
|
||||
<script lang="ts">
|
||||
import { onMount } from "svelte"
|
||||
import {
|
||||
ModalContent,
|
||||
|
@ -6,32 +6,52 @@
|
|||
Select,
|
||||
Body,
|
||||
Input,
|
||||
EnvDropdown,
|
||||
Modal,
|
||||
notifications,
|
||||
} from "@budibase/bbui"
|
||||
import { AUTH_TYPE_LABELS, AUTH_TYPES } from "./authTypes"
|
||||
import { BindableCombobox } from "@/components/common/bindings"
|
||||
import { getAuthBindings, getEnvironmentBindings } from "@/dataBinding"
|
||||
import { environment, licensing, auth } from "@/stores/portal"
|
||||
import CreateEditVariableModal from "@/components/portal/environment/CreateEditVariableModal.svelte"
|
||||
import { environment, licensing } from "@/stores/portal"
|
||||
import EnvVariableInput from "@/components/portal/environment/EnvVariableInput.svelte"
|
||||
|
||||
interface FormData {
|
||||
name?: string
|
||||
type?: string
|
||||
basic: {
|
||||
username?: string
|
||||
password?: string
|
||||
}
|
||||
bearer: {
|
||||
token?: string
|
||||
}
|
||||
}
|
||||
|
||||
export let configs
|
||||
export let currentConfig
|
||||
export let onConfirm
|
||||
export let onRemove
|
||||
|
||||
let form = {
|
||||
let form: FormData = {
|
||||
basic: {},
|
||||
bearer: {},
|
||||
}
|
||||
|
||||
let errors = {
|
||||
let errors: FormData = {
|
||||
basic: {},
|
||||
bearer: {},
|
||||
}
|
||||
|
||||
let blurred = {
|
||||
let blurred: {
|
||||
name?: boolean
|
||||
type?: boolean
|
||||
basic: {
|
||||
username?: boolean
|
||||
password?: boolean
|
||||
}
|
||||
bearer: {
|
||||
token?: boolean
|
||||
}
|
||||
} = {
|
||||
basic: {},
|
||||
bearer: {},
|
||||
}
|
||||
|
@ -39,19 +59,7 @@
|
|||
let hasErrors = false
|
||||
let hasChanged = false
|
||||
|
||||
let createVariableModal
|
||||
let formFieldkey
|
||||
|
||||
onMount(async () => {
|
||||
try {
|
||||
await environment.loadVariables()
|
||||
if ($auth.user) {
|
||||
await licensing.init()
|
||||
}
|
||||
} catch (err) {
|
||||
console.error(err)
|
||||
}
|
||||
|
||||
if (currentConfig) {
|
||||
deconstructConfig()
|
||||
}
|
||||
|
@ -79,7 +87,7 @@
|
|||
* map the form into a new config to save by type
|
||||
*/
|
||||
const constructConfig = () => {
|
||||
const newConfig = {
|
||||
const newConfig: any = {
|
||||
name: form.name,
|
||||
type: form.type,
|
||||
}
|
||||
|
@ -123,10 +131,10 @@
|
|||
errors.name =
|
||||
// check for duplicate excluding the current config
|
||||
configs.find(
|
||||
c => c.name === form.name && c.name !== currentConfig?.name
|
||||
(c: any) => c.name === form.name && c.name !== currentConfig?.name
|
||||
) !== undefined
|
||||
? "Name must be unique"
|
||||
: null
|
||||
: undefined
|
||||
}
|
||||
// Name required
|
||||
else {
|
||||
|
@ -137,17 +145,17 @@
|
|||
|
||||
// TYPE
|
||||
const typeError = () => {
|
||||
errors.type = form.type ? null : "Type is required"
|
||||
errors.type = form.type ? undefined : "Type is required"
|
||||
return !!errors.type
|
||||
}
|
||||
|
||||
// BASIC AUTH
|
||||
const basicAuthErrors = () => {
|
||||
errors.basic.username = form.basic.username
|
||||
? null
|
||||
? undefined
|
||||
: "Username is required"
|
||||
errors.basic.password = form.basic.password
|
||||
? null
|
||||
? undefined
|
||||
: "Password is required"
|
||||
|
||||
return !!(errors.basic.username || errors.basic.password || commonError)
|
||||
|
@ -155,7 +163,7 @@
|
|||
|
||||
// BEARER TOKEN
|
||||
const bearerTokenErrors = () => {
|
||||
errors.bearer.token = form.bearer.token ? null : "Token is required"
|
||||
errors.bearer.token = form.bearer.token ? undefined : "Token is required"
|
||||
return !!(errors.bearer.token || commonError)
|
||||
}
|
||||
|
||||
|
@ -169,16 +177,6 @@
|
|||
}
|
||||
}
|
||||
|
||||
const save = async data => {
|
||||
try {
|
||||
await environment.createVariable(data)
|
||||
form.basic[formFieldkey] = `{{ env.${data.name} }}`
|
||||
createVariableModal.hide()
|
||||
} catch (err) {
|
||||
notifications.error(`Failed to create variable: ${err.message}`)
|
||||
}
|
||||
}
|
||||
|
||||
const onFieldChange = () => {
|
||||
checkErrors()
|
||||
checkChanged()
|
||||
|
@ -188,15 +186,13 @@
|
|||
onConfirm(constructConfig())
|
||||
}
|
||||
|
||||
async function handleUpgradePanel() {
|
||||
await environment.upgradePanelOpened()
|
||||
$licensing.goToUpgradePage()
|
||||
}
|
||||
|
||||
function showModal(key) {
|
||||
formFieldkey = key
|
||||
createVariableModal.show()
|
||||
}
|
||||
onMount(async () => {
|
||||
try {
|
||||
await environment.loadVariables()
|
||||
} catch (error) {
|
||||
notifications.error(`Error getting environment variables - ${error}`)
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<ModalContent
|
||||
|
@ -221,7 +217,7 @@
|
|||
bind:value={form.name}
|
||||
on:change={onFieldChange}
|
||||
on:blur={() => (blurred.name = true)}
|
||||
error={blurred.name ? errors.name : null}
|
||||
error={blurred.name ? errors.name : undefined}
|
||||
/>
|
||||
<Select
|
||||
label="Type"
|
||||
|
@ -229,31 +225,24 @@
|
|||
on:change={onFieldChange}
|
||||
options={AUTH_TYPE_LABELS}
|
||||
on:blur={() => (blurred.type = true)}
|
||||
error={blurred.type ? errors.type : null}
|
||||
error={blurred.type ? errors.type : undefined}
|
||||
/>
|
||||
{#if form.type === AUTH_TYPES.BASIC}
|
||||
<EnvDropdown
|
||||
<EnvVariableInput
|
||||
label="Username"
|
||||
bind:value={form.basic.username}
|
||||
on:change={onFieldChange}
|
||||
on:blur={() => (blurred.basic.username = true)}
|
||||
error={blurred.basic.username ? errors.basic.username : null}
|
||||
showModal={() => showModal("configKey")}
|
||||
variables={$environment.variables}
|
||||
environmentVariablesEnabled={$licensing.environmentVariablesEnabled}
|
||||
{handleUpgradePanel}
|
||||
error={blurred.basic.username ? errors.basic.username : undefined}
|
||||
/>
|
||||
<EnvDropdown
|
||||
|
||||
<EnvVariableInput
|
||||
label="Password"
|
||||
type="password"
|
||||
bind:value={form.basic.password}
|
||||
on:change={onFieldChange}
|
||||
on:blur={() => (blurred.basic.password = true)}
|
||||
error={blurred.basic.password ? errors.basic.password : null}
|
||||
showModal={() => showModal("configKey")}
|
||||
variables={$environment.variables}
|
||||
environmentVariablesEnabled={$licensing.environmentVariablesEnabled}
|
||||
{handleUpgradePanel}
|
||||
error={blurred.basic.password ? errors.basic.password : undefined}
|
||||
/>
|
||||
{/if}
|
||||
{#if form.type === AUTH_TYPES.BEARER}
|
||||
|
@ -274,16 +263,10 @@
|
|||
blurred.bearer.token = true
|
||||
onFieldChange()
|
||||
}}
|
||||
allowJS={false}
|
||||
placeholder="Token"
|
||||
appendBindingsAsOptions={true}
|
||||
drawerEnabled={false}
|
||||
error={blurred.bearer.token ? errors.bearer.token : null}
|
||||
/>
|
||||
{/if}
|
||||
</Layout>
|
||||
</ModalContent>
|
||||
|
||||
<Modal bind:this={createVariableModal}>
|
||||
<CreateEditVariableModal {save} />
|
||||
</Modal>
|
||||
|
|
|
@ -1,14 +1,15 @@
|
|||
<script>
|
||||
import { Heading, Layout } from "@budibase/bbui"
|
||||
import { Heading, Layout, notifications } from "@budibase/bbui"
|
||||
import KeyValueBuilder from "@/components/integration/KeyValueBuilder.svelte"
|
||||
import ViewDynamicVariables from "./ViewDynamicVariables.svelte"
|
||||
import { getEnvironmentBindings } from "@/dataBinding"
|
||||
import { licensing } from "@/stores/portal"
|
||||
import { environment, licensing } from "@/stores/portal"
|
||||
import { queries } from "@/stores/builder"
|
||||
import { cloneDeep } from "lodash/fp"
|
||||
import SaveDatasourceButton from "../SaveDatasourceButton.svelte"
|
||||
import Panel from "../Panel.svelte"
|
||||
import Tooltip from "../Tooltip.svelte"
|
||||
import { onMount } from "svelte"
|
||||
|
||||
export let datasource
|
||||
|
||||
|
@ -27,6 +28,14 @@
|
|||
|
||||
updatedDatasource.config.staticVariables = newStaticVariables
|
||||
}
|
||||
|
||||
onMount(async () => {
|
||||
try {
|
||||
await environment.loadVariables()
|
||||
} catch (error) {
|
||||
notifications.error(`Error getting environment variables - ${error}`)
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<Panel>
|
||||
|
|
|
@ -55,6 +55,11 @@
|
|||
active={$isActive("./oauth2")}
|
||||
/>
|
||||
{/if}
|
||||
<SideNavItem
|
||||
text="App scripts"
|
||||
url={$url("./scripts")}
|
||||
active={$isActive("./scripts")}
|
||||
/>
|
||||
<div class="delete-action">
|
||||
<AbsTooltip
|
||||
position={TooltipPosition.Bottom}
|
||||
|
|
|
@ -170,7 +170,7 @@
|
|||
<Layout noPadding>
|
||||
<Layout gap="XS" noPadding>
|
||||
<Heading>Automations</Heading>
|
||||
<Body size="S">See your automation history and edit advanced settings</Body>
|
||||
<Body>See your automation history and edit advanced settings</Body>
|
||||
</Layout>
|
||||
<Divider />
|
||||
|
||||
|
@ -251,7 +251,6 @@
|
|||
data={runHistory}
|
||||
{customRenderers}
|
||||
placeholderText="No history found"
|
||||
border={false}
|
||||
/>
|
||||
<div class="pagination">
|
||||
<Pagination
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
Tags,
|
||||
Tag,
|
||||
Table,
|
||||
ButtonGroup,
|
||||
} from "@budibase/bbui"
|
||||
import { backups, licensing, auth, admin } from "@/stores/portal"
|
||||
import { appStore } from "@/stores/builder"
|
||||
|
@ -180,25 +181,17 @@
|
|||
{#if !$auth.accountPortalAccess && $admin.cloud}
|
||||
<Body>Contact your account holder to upgrade your plan.</Body>
|
||||
{/if}
|
||||
<div class="pro-buttons">
|
||||
{#if $auth.accountPortalAccess}
|
||||
<Button
|
||||
primary
|
||||
disabled={!$auth.accountPortalAccess && $admin.cloud}
|
||||
on:click={$licensing.goToUpgradePage()}
|
||||
>
|
||||
Upgrade
|
||||
</Button>
|
||||
<ButtonGroup>
|
||||
{#if $admin.cloud && $auth.accountPortalAccess}
|
||||
<Button primary on:click={$licensing.goToUpgradePage}>Upgrade</Button>
|
||||
{/if}
|
||||
<Button
|
||||
secondary
|
||||
on:click={() => {
|
||||
window.open("https://budibase.com/pricing/", "_blank")
|
||||
}}
|
||||
on:click={() => window.open("https://budibase.com/pricing/", "_blank")}
|
||||
>
|
||||
View plans
|
||||
</Button>
|
||||
</div>
|
||||
</ButtonGroup>
|
||||
{:else if !backupData?.length && !loading && !filterOpt && !dateRange?.length}
|
||||
<div class="center">
|
||||
<Layout noPadding gap="S" justifyItems="center">
|
||||
|
@ -311,12 +304,7 @@
|
|||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
gap: var(--spacing-m);
|
||||
}
|
||||
|
||||
.pro-buttons {
|
||||
display: flex;
|
||||
gap: var(--spacing-m);
|
||||
gap: var(--spacing-l);
|
||||
}
|
||||
|
||||
.center {
|
||||
|
|
|
@ -0,0 +1,202 @@
|
|||
<script lang="ts">
|
||||
import {
|
||||
Body,
|
||||
Button,
|
||||
Link,
|
||||
Divider,
|
||||
Heading,
|
||||
Layout,
|
||||
Table,
|
||||
Label,
|
||||
Input,
|
||||
TextArea,
|
||||
Select,
|
||||
Helpers,
|
||||
notifications,
|
||||
Tag,
|
||||
Tags,
|
||||
ButtonGroup,
|
||||
} from "@budibase/bbui"
|
||||
import { appStore } from "@/stores/builder"
|
||||
import { type AppScript } from "@budibase/types"
|
||||
import { getSequentialName } from "@/helpers/duplicate"
|
||||
import ConfirmDialog from "@/components/common/ConfirmDialog.svelte"
|
||||
import { licensing, auth, admin } from "@/stores/portal"
|
||||
|
||||
const schema = {
|
||||
name: {
|
||||
type: "string",
|
||||
label: "Name",
|
||||
},
|
||||
location: {
|
||||
type: "string",
|
||||
label: "Location",
|
||||
},
|
||||
}
|
||||
|
||||
let selectedScript: AppScript | undefined
|
||||
let isNew = false
|
||||
let confirmDeleteModal: any
|
||||
|
||||
$: nameError = selectedScript?.name ? undefined : "Please enter a name"
|
||||
$: invalid = !!nameError
|
||||
$: enabled = $licensing.customAppScriptsEnabled
|
||||
|
||||
const addScript = () => {
|
||||
const name = getSequentialName($appStore.scripts, "Script ", {
|
||||
getName: script => script.name,
|
||||
numberFirstItem: true,
|
||||
})
|
||||
selectedScript = { id: Helpers.uuid(), location: "Head", name }
|
||||
isNew = true
|
||||
}
|
||||
|
||||
const editScript = (e: any) => {
|
||||
selectedScript = { ...(e.detail as AppScript) }
|
||||
isNew = false
|
||||
}
|
||||
|
||||
const cancel = () => {
|
||||
selectedScript = undefined
|
||||
}
|
||||
|
||||
const save = async () => {
|
||||
if (!selectedScript) {
|
||||
return
|
||||
}
|
||||
const newScripts = $appStore.scripts
|
||||
.filter(script => script.id !== selectedScript!.id)
|
||||
.concat([selectedScript])
|
||||
await appStore.updateApp({ scripts: newScripts })
|
||||
notifications.success("Script saved successfully")
|
||||
selectedScript = undefined
|
||||
}
|
||||
|
||||
const requestDeletion = () => {
|
||||
confirmDeleteModal?.show()
|
||||
}
|
||||
|
||||
const deleteScript = async () => {
|
||||
if (!selectedScript) {
|
||||
return
|
||||
}
|
||||
const newScripts = $appStore.scripts.filter(
|
||||
script => script.id !== selectedScript!.id
|
||||
)
|
||||
await appStore.updateApp({ scripts: newScripts })
|
||||
notifications.success("Script deleted successfully")
|
||||
selectedScript = undefined
|
||||
}
|
||||
</script>
|
||||
|
||||
<Layout noPadding>
|
||||
<Layout gap="XS" noPadding>
|
||||
<div class="title">
|
||||
<Heading>App scripts</Heading>
|
||||
{#if !enabled}
|
||||
<Tags>
|
||||
<Tag icon="LockClosed">Enterprise</Tag>
|
||||
</Tags>
|
||||
{/if}
|
||||
</div>
|
||||
<div class="subtitle">
|
||||
<Body>
|
||||
Inject analytics, scripts or stylesheets into your app<br />
|
||||
<Link href="https://docs.budibase.com/docs/app-scripts" target="_blank">
|
||||
Learn more about script injection in the docs
|
||||
</Link>
|
||||
</Body>
|
||||
{#if !selectedScript && enabled}
|
||||
<Button cta on:click={addScript}>Add script</Button>
|
||||
{/if}
|
||||
</div>
|
||||
</Layout>
|
||||
<Divider />
|
||||
{#if !enabled}
|
||||
{#if $admin.cloud && !$auth.accountPortalAccess}
|
||||
<Body>Contact your account holder to upgrade your plan.</Body>
|
||||
{/if}
|
||||
<ButtonGroup>
|
||||
{#if $admin.cloud && $auth.accountPortalAccess}
|
||||
<Button cta on:click={$licensing.goToUpgradePage}>Upgrade</Button>
|
||||
{/if}
|
||||
<Button
|
||||
secondary
|
||||
on:click={() => window.open("https://budibase.com/pricing/", "_blank")}
|
||||
>
|
||||
View plans
|
||||
</Button>
|
||||
</ButtonGroup>
|
||||
{:else if selectedScript}
|
||||
<Heading size="S">{isNew ? "Add new script" : "Edit script"}</Heading>
|
||||
<div class="form">
|
||||
<Label size="L">Name</Label>
|
||||
<Input bind:value={selectedScript.name} error={nameError} />
|
||||
<Label size="L">Location</Label>
|
||||
<Select
|
||||
bind:value={selectedScript.location}
|
||||
options={["Head", "Body"]}
|
||||
placeholder={false}
|
||||
/>
|
||||
<Label size="L">HTML</Label>
|
||||
<TextArea
|
||||
bind:value={selectedScript.html}
|
||||
minHeight={200}
|
||||
placeholder="<script>...</script>"
|
||||
/>
|
||||
<div />
|
||||
<div class="buttons">
|
||||
{#if !isNew}
|
||||
<Button warning quiet on:click={requestDeletion}>Delete</Button>
|
||||
{/if}
|
||||
<Button secondary on:click={cancel}>Cancel</Button>
|
||||
<Button cta disabled={invalid} on:click={save}>Save</Button>
|
||||
</div>
|
||||
</div>
|
||||
{:else}
|
||||
<Table
|
||||
on:click={editScript}
|
||||
{schema}
|
||||
data={$appStore.scripts}
|
||||
allowSelectRows={false}
|
||||
allowEditColumns={false}
|
||||
allowEditRows={false}
|
||||
placeholderText="You haven't added any scripts yet"
|
||||
/>
|
||||
{/if}
|
||||
</Layout>
|
||||
|
||||
<ConfirmDialog
|
||||
bind:this={confirmDeleteModal}
|
||||
title="Delete script"
|
||||
body="Are you sure you want to delete this script?"
|
||||
onOk={deleteScript}
|
||||
/>
|
||||
|
||||
<style>
|
||||
.title,
|
||||
.subtitle {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: flex-start;
|
||||
align-items: center;
|
||||
gap: var(--spacing-l);
|
||||
}
|
||||
.subtitle {
|
||||
justify-content: space-between;
|
||||
}
|
||||
.form {
|
||||
display: grid;
|
||||
grid-template-columns: 100px 480px;
|
||||
row-gap: var(--spacing-l);
|
||||
}
|
||||
.form :global(.spectrum-FieldLabel) {
|
||||
padding-top: 7px;
|
||||
}
|
||||
.buttons {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: flex-end;
|
||||
gap: var(--spacing-l);
|
||||
}
|
||||
</style>
|
|
@ -24,7 +24,6 @@
|
|||
auth,
|
||||
admin,
|
||||
licensing,
|
||||
environment,
|
||||
enrichedApps,
|
||||
sortBy,
|
||||
} from "@/stores/portal"
|
||||
|
@ -162,7 +161,6 @@
|
|||
|
||||
onMount(async () => {
|
||||
try {
|
||||
await environment.loadVariables()
|
||||
// If the portal is loaded from an external URL with a template param
|
||||
const initInfo = await auth.getInitInfo()
|
||||
if (initInfo?.init_template) {
|
||||
|
|
|
@ -4,9 +4,12 @@ import {
|
|||
App,
|
||||
AppFeatures,
|
||||
AppIcon,
|
||||
AppScript,
|
||||
AutomationSettings,
|
||||
Plugin,
|
||||
UpdateAppRequest,
|
||||
} from "@budibase/types"
|
||||
import { get } from "svelte/store"
|
||||
|
||||
interface ClientFeatures {
|
||||
spectrumThemes: boolean
|
||||
|
@ -46,6 +49,7 @@ interface AppMetaState {
|
|||
revertableVersion?: string
|
||||
upgradableVersion?: string
|
||||
icon?: AppIcon
|
||||
scripts: AppScript[]
|
||||
}
|
||||
|
||||
export const INITIAL_APP_META_STATE: AppMetaState = {
|
||||
|
@ -79,6 +83,7 @@ export const INITIAL_APP_META_STATE: AppMetaState = {
|
|||
usedPlugins: [],
|
||||
automations: {},
|
||||
routes: {},
|
||||
scripts: [],
|
||||
}
|
||||
|
||||
export class AppMetaStore extends BudiStore<AppMetaState> {
|
||||
|
@ -90,20 +95,12 @@ export class AppMetaStore extends BudiStore<AppMetaState> {
|
|||
this.store.set({ ...INITIAL_APP_META_STATE })
|
||||
}
|
||||
|
||||
syncAppPackage(pkg: {
|
||||
application: App
|
||||
clientLibPath: string
|
||||
hasLock: boolean
|
||||
}) {
|
||||
const { application: app, clientLibPath, hasLock } = pkg
|
||||
|
||||
syncApp(app: App) {
|
||||
this.update(state => ({
|
||||
...state,
|
||||
name: app.name,
|
||||
appId: app.appId,
|
||||
url: app.url || "",
|
||||
hasLock,
|
||||
clientLibPath,
|
||||
libraries: app.componentLibraries,
|
||||
version: app.version,
|
||||
appInstance: app.instance,
|
||||
|
@ -118,9 +115,24 @@ export class AppMetaStore extends BudiStore<AppMetaState> {
|
|||
initialised: true,
|
||||
automations: app.automations || {},
|
||||
hasAppPackage: true,
|
||||
scripts: app.scripts || [],
|
||||
}))
|
||||
}
|
||||
|
||||
syncAppPackage(pkg: {
|
||||
application: App
|
||||
clientLibPath: string
|
||||
hasLock: boolean
|
||||
}) {
|
||||
const { application, clientLibPath, hasLock } = pkg
|
||||
this.update(state => ({
|
||||
...state,
|
||||
hasLock,
|
||||
clientLibPath,
|
||||
}))
|
||||
this.syncApp(application)
|
||||
}
|
||||
|
||||
syncClientFeatures(features: Partial<ClientFeatures>) {
|
||||
this.update(state => ({
|
||||
...state,
|
||||
|
@ -146,6 +158,11 @@ export class AppMetaStore extends BudiStore<AppMetaState> {
|
|||
}))
|
||||
}
|
||||
|
||||
async updateApp(updates: UpdateAppRequest) {
|
||||
const app = await API.saveAppMetadata(get(this.store).appId, updates)
|
||||
this.syncApp(app)
|
||||
}
|
||||
|
||||
// Returned from socket
|
||||
syncMetadata(metadata: { name: string; url: string; icon?: AppIcon }) {
|
||||
const { name, url, icon } = metadata
|
||||
|
|
|
@ -36,6 +36,7 @@ interface LicensingState {
|
|||
budibaseAIEnabled: boolean
|
||||
customAIConfigsEnabled: boolean
|
||||
auditLogsEnabled: boolean
|
||||
customAppScriptsEnabled: boolean
|
||||
syncAutomationsEnabled: boolean
|
||||
triggerAutomationRunEnabled: boolean
|
||||
// the currently used quotas from the db
|
||||
|
@ -77,6 +78,7 @@ class LicensingStore extends BudiStore<LicensingState> {
|
|||
budibaseAIEnabled: false,
|
||||
customAIConfigsEnabled: false,
|
||||
auditLogsEnabled: false,
|
||||
customAppScriptsEnabled: false,
|
||||
syncAutomationsEnabled: false,
|
||||
triggerAutomationRunEnabled: false,
|
||||
// the currently used quotas from the db
|
||||
|
@ -182,6 +184,9 @@ class LicensingStore extends BudiStore<LicensingState> {
|
|||
const customAIConfigsEnabled = features.includes(
|
||||
Constants.Features.AI_CUSTOM_CONFIGS
|
||||
)
|
||||
const customAppScriptsEnabled = features.includes(
|
||||
Constants.Features.CUSTOM_APP_SCRIPTS
|
||||
)
|
||||
this.update(state => {
|
||||
return {
|
||||
...state,
|
||||
|
@ -202,6 +207,7 @@ class LicensingStore extends BudiStore<LicensingState> {
|
|||
syncAutomationsEnabled,
|
||||
triggerAutomationRunEnabled,
|
||||
perAppBuildersEnabled,
|
||||
customAppScriptsEnabled,
|
||||
}
|
||||
})
|
||||
}
|
||||
|
|
|
@ -154,6 +154,21 @@ const requiresMigration = async (ctx: Ctx) => {
|
|||
return latestMigrationApplied !== latestMigration
|
||||
}
|
||||
|
||||
const getAppScriptHTML = (
|
||||
app: App,
|
||||
location: "Head" | "Body",
|
||||
nonce: string
|
||||
) => {
|
||||
if (!app.scripts?.length) {
|
||||
return ""
|
||||
}
|
||||
return app.scripts
|
||||
.filter(script => script.location === location && script.html?.length)
|
||||
.map(script => script.html)
|
||||
.join("\n")
|
||||
.replaceAll("<script", `<script nonce="${nonce}"`)
|
||||
}
|
||||
|
||||
export const serveApp = async function (ctx: UserCtx<void, ServeAppResponse>) {
|
||||
if (ctx.url.includes("apple-touch-icon.png")) {
|
||||
ctx.redirect("/builder/bblogo.png")
|
||||
|
@ -190,6 +205,9 @@ export const serveApp = async function (ctx: UserCtx<void, ServeAppResponse>) {
|
|||
const hideFooter =
|
||||
ctx?.user?.license?.features?.includes(Feature.BRANDING) || false
|
||||
const themeVariables = getThemeVariables(appInfo?.theme)
|
||||
const addAppScripts =
|
||||
ctx?.user?.license?.features?.includes(Feature.CUSTOM_APP_SCRIPTS) ||
|
||||
false
|
||||
|
||||
if (!env.isJest()) {
|
||||
const plugins = await objectStore.enrichPluginURLs(appInfo.usedPlugins)
|
||||
|
@ -199,7 +217,8 @@ export const serveApp = async function (ctx: UserCtx<void, ServeAppResponse>) {
|
|||
* BudibaseApp.svelte file as we can never detect if the types are correct. To get around this
|
||||
* I've created a type which expects what the app will expect to receive.
|
||||
*/
|
||||
const props: BudibaseAppProps = {
|
||||
const nonce = ctx.state.nonce || ""
|
||||
let props: BudibaseAppProps = {
|
||||
title: branding?.platformTitle || `${appInfo.name}`,
|
||||
showSkeletonLoader: appInfo.features?.skeletonLoader ?? false,
|
||||
hideDevTools,
|
||||
|
@ -218,7 +237,13 @@ export const serveApp = async function (ctx: UserCtx<void, ServeAppResponse>) {
|
|||
? await objectStore.getGlobalFileUrl("settings", "faviconUrl")
|
||||
: "",
|
||||
appMigrating: needMigrations,
|
||||
nonce: ctx.state.nonce,
|
||||
nonce,
|
||||
}
|
||||
|
||||
// Add custom app scripts if enabled
|
||||
if (addAppScripts) {
|
||||
props.headAppScripts = getAppScriptHTML(appInfo, "Head", nonce)
|
||||
props.bodyAppScripts = getAppScriptHTML(appInfo, "Body", nonce)
|
||||
}
|
||||
|
||||
const { head, html, css } = AppComponent.render({ props })
|
||||
|
@ -273,10 +298,22 @@ export const serveBuilderPreview = async function (
|
|||
const templateLoc = join(__dirname, "templates")
|
||||
const previewLoc = fs.existsSync(templateLoc) ? templateLoc : __dirname
|
||||
const previewHbs = loadHandlebarsFile(join(previewLoc, "preview.hbs"))
|
||||
ctx.body = await processString(previewHbs, {
|
||||
const nonce = ctx.state.nonce || ""
|
||||
const addAppScripts =
|
||||
ctx?.user?.license?.features?.includes(Feature.CUSTOM_APP_SCRIPTS) ||
|
||||
false
|
||||
let props: any = {
|
||||
clientLibPath: objectStore.clientLibraryUrl(appId!, appInfo.version),
|
||||
nonce: ctx.state.nonce,
|
||||
})
|
||||
nonce,
|
||||
}
|
||||
|
||||
// Add custom app scripts if enabled
|
||||
if (addAppScripts) {
|
||||
props.headAppScripts = getAppScriptHTML(appInfo, "Head", nonce)
|
||||
props.bodyAppScripts = getAppScriptHTML(appInfo, "Body", nonce)
|
||||
}
|
||||
|
||||
ctx.body = await processString(previewHbs, props)
|
||||
} else {
|
||||
// just return the app info for jest to assert on
|
||||
ctx.body = { ...appInfo, builderPreview: true }
|
||||
|
|
|
@ -88,6 +88,9 @@
|
|||
font-weight: 400;
|
||||
}
|
||||
</style>
|
||||
|
||||
<!-- eslint-disable-next-line svelte/no-at-html-tags -->
|
||||
{@html props.headAppScripts || ""}
|
||||
</svelte:head>
|
||||
|
||||
<body id="app">
|
||||
|
@ -135,4 +138,7 @@
|
|||
document.getElementById("error").style.display = "flex"
|
||||
}
|
||||
</script>
|
||||
|
||||
<!-- eslint-disable-next-line svelte/no-at-html-tags -->
|
||||
{@html props.bodyAppScripts || ""}
|
||||
</body>
|
||||
|
|
|
@ -5,14 +5,12 @@
|
|||
})
|
||||
</script>
|
||||
<head>
|
||||
<script nonce="{{ nonce }}">
|
||||
window["##BUDIBASE_APP_ID##"] = "{{appId}}"
|
||||
window["##BUDIBASE_APP_EMBEDDED##"] = "{{embedded}}"
|
||||
</script>
|
||||
{{{head}}}
|
||||
<style>{{{css}}}</style>
|
||||
</head>
|
||||
|
||||
<script nonce="{{ nonce }}">
|
||||
window["##BUDIBASE_APP_ID##"] = "{{appId}}"
|
||||
window["##BUDIBASE_APP_EMBEDDED##"] = "{{embedded}}"
|
||||
</script>
|
||||
|
||||
{{{body}}}
|
||||
</html>
|
||||
|
|
|
@ -108,6 +108,9 @@
|
|||
window.addEventListener("message", receiveMessage)
|
||||
window.parent.postMessage({ type: "ready" })
|
||||
</script>
|
||||
{{ headAppScripts }}
|
||||
</head>
|
||||
<body></body>
|
||||
<body>
|
||||
{{ bodyAppScripts }}
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
@ -29,6 +29,7 @@ export interface App extends Document {
|
|||
snippets?: Snippet[]
|
||||
creationVersion?: string
|
||||
updatedBy?: string
|
||||
scripts?: AppScript[]
|
||||
}
|
||||
|
||||
export interface AppInstance {
|
||||
|
@ -82,3 +83,10 @@ export interface AppFeatures {
|
|||
export interface AutomationSettings {
|
||||
chainAutomations?: boolean
|
||||
}
|
||||
|
||||
export interface AppScript {
|
||||
id: string
|
||||
name: string
|
||||
location: "Head" | "Body"
|
||||
html?: string
|
||||
}
|
||||
|
|
|
@ -13,6 +13,7 @@ export enum Feature {
|
|||
APP_BUILDERS = "appBuilders",
|
||||
OFFLINE = "offline",
|
||||
EXPANDED_PUBLIC_API = "expandedPublicApi",
|
||||
CUSTOM_APP_SCRIPTS = "customAppScripts",
|
||||
// deprecated - no longer licensed
|
||||
VIEW_PERMISSIONS = "viewPermissions",
|
||||
VIEW_READONLY_COLUMNS = "viewReadonlyColumns",
|
||||
|
|
|
@ -14,4 +14,6 @@ export interface BudibaseAppProps {
|
|||
sideNav?: boolean
|
||||
hideFooter?: boolean
|
||||
nonce?: string
|
||||
headAppScripts?: string
|
||||
bodyAppScripts?: string
|
||||
}
|
||||
|
|
15
yarn.lock
15
yarn.lock
|
@ -2699,17 +2699,10 @@
|
|||
resolved "https://registry.yarnpkg.com/@babel/regjsgen/-/regjsgen-0.8.0.tgz#f0ba69b075e1f05fb2825b7fad991e7adbb18310"
|
||||
integrity sha512-x/rqGMdzj+fWZvCOYForTghzbtqPDZ5gPwaoNGHdgDfF2QA/XZbCBp4Moo5scrkAMPhB7z26XM/AaHuIJdgauA==
|
||||
|
||||
"@babel/runtime@^7.10.5", "@babel/runtime@^7.12.5", "@babel/runtime@^7.15.4", "@babel/runtime@^7.8.4", "@babel/runtime@^7.9.2":
|
||||
version "7.25.6"
|
||||
resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.25.6.tgz#9afc3289f7184d8d7f98b099884c26317b9264d2"
|
||||
integrity sha512-VBj9MYyDb9tuLq7yzqjgzt6Q+IBQLrGZfdjOekyEirZPHxXWoTSGUTMrpsfi58Up73d13NfYLv8HT9vmznjzhQ==
|
||||
dependencies:
|
||||
regenerator-runtime "^0.14.0"
|
||||
|
||||
"@babel/runtime@^7.8.3":
|
||||
version "7.26.9"
|
||||
resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.26.9.tgz#aa4c6facc65b9cb3f87d75125ffd47781b475433"
|
||||
integrity sha512-aA63XwOkcl4xxQa3HjPMqOP6LiK0ZDv3mUPYEFXkpHbaFjtGggE1A61FjFzJnB+p7/oy2gA8E+rcBNl/zC1tMg==
|
||||
"@babel/runtime@^7.10.5", "@babel/runtime@^7.12.5", "@babel/runtime@^7.15.4", "@babel/runtime@^7.8.3", "@babel/runtime@^7.8.4", "@babel/runtime@^7.9.2":
|
||||
version "7.26.10"
|
||||
resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.26.10.tgz#a07b4d8fa27af131a633d7b3524db803eb4764c2"
|
||||
integrity sha512-2WJMeRQPHKSPemqk/awGrAiuFfzBmOIPXKizAsVhWH9YJqLZ0H+HS4c8loHGgW6utJ3E/ejXQUsiGaQy2NZ9Fw==
|
||||
dependencies:
|
||||
regenerator-runtime "^0.14.0"
|
||||
|
||||
|
|
Loading…
Reference in New Issue