Merge branch 'master' into ts/type-fetchData-usages
This commit is contained in:
commit
0f069b77fc
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"$schema": "node_modules/lerna/schemas/lerna-schema.json",
|
"$schema": "node_modules/lerna/schemas/lerna-schema.json",
|
||||||
"version": "3.2.41",
|
"version": "3.2.43",
|
||||||
"npmClient": "yarn",
|
"npmClient": "yarn",
|
||||||
"concurrency": 20,
|
"concurrency": 20,
|
||||||
"command": {
|
"command": {
|
||||||
|
|
|
@ -21,7 +21,7 @@
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"prebuild": "rimraf dist/",
|
"prebuild": "rimraf dist/",
|
||||||
"prepack": "cp package.json dist",
|
"prepack": "cp package.json dist",
|
||||||
"build": "node ./scripts/build.js && tsc -p tsconfig.build.json --emitDeclarationOnly --paths null",
|
"build": "node ./scripts/build.js && tsc -p tsconfig.build.json --emitDeclarationOnly --paths null && tsc -p tsconfig.test.json --paths null",
|
||||||
"build:dev": "yarn prebuild && tsc --build --watch --preserveWatchOutput",
|
"build:dev": "yarn prebuild && tsc --build --watch --preserveWatchOutput",
|
||||||
"build:oss": "node ./scripts/build.js",
|
"build:oss": "node ./scripts/build.js",
|
||||||
"check:types": "tsc -p tsconfig.json --noEmit --paths null",
|
"check:types": "tsc -p tsconfig.json --noEmit --paths null",
|
||||||
|
|
|
@ -0,0 +1,9 @@
|
||||||
|
{
|
||||||
|
"extends": "../../tsconfig.build.json",
|
||||||
|
"compilerOptions": {
|
||||||
|
"outDir": "dist",
|
||||||
|
"sourceMap": true
|
||||||
|
},
|
||||||
|
"include": ["tests/**/*.js", "tests/**/*.ts"],
|
||||||
|
"exclude": ["node_modules", "dist"]
|
||||||
|
}
|
|
@ -28,7 +28,9 @@
|
||||||
let loading = false
|
let loading = false
|
||||||
let deleteConfirmationDialog
|
let deleteConfirmationDialog
|
||||||
|
|
||||||
$: defaultName = getSequentialName($snippets, "MySnippet", x => x.name)
|
$: defaultName = getSequentialName($snippets, "MySnippet", {
|
||||||
|
getName: x => x.name,
|
||||||
|
})
|
||||||
$: key = snippet?.name
|
$: key = snippet?.name
|
||||||
$: name = snippet?.name || defaultName
|
$: name = snippet?.name || defaultName
|
||||||
$: code = snippet?.code ? encodeJSBinding(snippet.code) : ""
|
$: code = snippet?.code ? encodeJSBinding(snippet.code) : ""
|
||||||
|
|
|
@ -16,7 +16,10 @@ export {
|
||||||
|
|
||||||
export const AUTO_COLUMN_SUB_TYPES = AutoFieldSubType
|
export const AUTO_COLUMN_SUB_TYPES = AutoFieldSubType
|
||||||
|
|
||||||
export const AUTO_COLUMN_DISPLAY_NAMES = {
|
export const AUTO_COLUMN_DISPLAY_NAMES: Record<
|
||||||
|
keyof typeof AUTO_COLUMN_SUB_TYPES,
|
||||||
|
string
|
||||||
|
> = {
|
||||||
AUTO_ID: "Auto ID",
|
AUTO_ID: "Auto ID",
|
||||||
CREATED_BY: "Created By",
|
CREATED_BY: "Created By",
|
||||||
CREATED_AT: "Created At",
|
CREATED_AT: "Created At",
|
||||||
|
@ -209,13 +212,6 @@ export const Roles = {
|
||||||
BUILDER: "BUILDER",
|
BUILDER: "BUILDER",
|
||||||
}
|
}
|
||||||
|
|
||||||
export function isAutoColumnUserRelationship(subtype) {
|
|
||||||
return (
|
|
||||||
subtype === AUTO_COLUMN_SUB_TYPES.CREATED_BY ||
|
|
||||||
subtype === AUTO_COLUMN_SUB_TYPES.UPDATED_BY
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
export const PrettyRelationshipDefinitions = {
|
export const PrettyRelationshipDefinitions = {
|
||||||
MANY: "Many rows",
|
MANY: "Many rows",
|
||||||
ONE: "One row",
|
ONE: "One row",
|
|
@ -10,13 +10,13 @@
|
||||||
*
|
*
|
||||||
* Repl
|
* Repl
|
||||||
*/
|
*/
|
||||||
export const duplicateName = (name, allNames) => {
|
export const duplicateName = (name: string, allNames: string[]) => {
|
||||||
const duplicatePattern = new RegExp(`\\s(\\d+)$`)
|
const duplicatePattern = new RegExp(`\\s(\\d+)$`)
|
||||||
const baseName = name.split(duplicatePattern)[0]
|
const baseName = name.split(duplicatePattern)[0]
|
||||||
const isDuplicate = new RegExp(`${baseName}\\s(\\d+)$`)
|
const isDuplicate = new RegExp(`${baseName}\\s(\\d+)$`)
|
||||||
|
|
||||||
// get the sequence from matched names
|
// get the sequence from matched names
|
||||||
const sequence = []
|
const sequence: number[] = []
|
||||||
allNames.filter(n => {
|
allNames.filter(n => {
|
||||||
if (n === baseName) {
|
if (n === baseName) {
|
||||||
return true
|
return true
|
||||||
|
@ -70,12 +70,18 @@ export const duplicateName = (name, allNames) => {
|
||||||
* @param getName optional function to extract the name for an item, if not a
|
* @param getName optional function to extract the name for an item, if not a
|
||||||
* flat array of strings
|
* flat array of strings
|
||||||
*/
|
*/
|
||||||
export const getSequentialName = (
|
export const getSequentialName = <T extends any>(
|
||||||
items,
|
items: T[] | null,
|
||||||
prefix,
|
prefix: string | null,
|
||||||
{ getName = x => x, numberFirstItem = false } = {}
|
{
|
||||||
|
getName,
|
||||||
|
numberFirstItem,
|
||||||
|
}: {
|
||||||
|
getName?: (item: T) => string
|
||||||
|
numberFirstItem?: boolean
|
||||||
|
} = {}
|
||||||
) => {
|
) => {
|
||||||
if (!prefix?.length || !getName) {
|
if (!prefix?.length) {
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
const trimmedPrefix = prefix.trim()
|
const trimmedPrefix = prefix.trim()
|
||||||
|
@ -85,7 +91,7 @@ export const getSequentialName = (
|
||||||
}
|
}
|
||||||
let max = 0
|
let max = 0
|
||||||
items.forEach(item => {
|
items.forEach(item => {
|
||||||
const name = getName(item)
|
const name = getName?.(item) ?? item
|
||||||
if (typeof name !== "string" || !name.startsWith(trimmedPrefix)) {
|
if (typeof name !== "string" || !name.startsWith(trimmedPrefix)) {
|
||||||
return
|
return
|
||||||
}
|
}
|
|
@ -1,7 +1,8 @@
|
||||||
|
import { FeatureFlag } from "@budibase/types"
|
||||||
import { auth } from "../stores/portal"
|
import { auth } from "../stores/portal"
|
||||||
import { get } from "svelte/store"
|
import { get } from "svelte/store"
|
||||||
|
|
||||||
export const isEnabled = featureFlag => {
|
export const isEnabled = (featureFlag: FeatureFlag | `${FeatureFlag}`) => {
|
||||||
const user = get(auth).user
|
const user = get(auth).user
|
||||||
return !!user?.flags?.[featureFlag]
|
return !!user?.flags?.[featureFlag]
|
||||||
}
|
}
|
|
@ -1,13 +1,21 @@
|
||||||
import { writable } from "svelte/store"
|
import { writable } from "svelte/store"
|
||||||
import { API } from "@/api"
|
import { API } from "@/api"
|
||||||
|
|
||||||
export default function (url) {
|
export default function (url: string) {
|
||||||
const store = writable({ status: "LOADING", data: {}, error: {} })
|
const store = writable<{
|
||||||
|
status: "LOADING" | "SUCCESS" | "ERROR"
|
||||||
|
data: object
|
||||||
|
error?: unknown
|
||||||
|
}>({
|
||||||
|
status: "LOADING",
|
||||||
|
data: {},
|
||||||
|
error: {},
|
||||||
|
})
|
||||||
|
|
||||||
async function get() {
|
async function get() {
|
||||||
store.update(u => ({ ...u, status: "LOADING" }))
|
store.update(u => ({ ...u, status: "LOADING" }))
|
||||||
try {
|
try {
|
||||||
const data = await API.get({ url })
|
const data = await API.get<object>({ url })
|
||||||
store.set({ data, status: "SUCCESS" })
|
store.set({ data, status: "SUCCESS" })
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
store.set({ data: {}, error: e, status: "ERROR" })
|
store.set({ data: {}, error: e, status: "ERROR" })
|
|
@ -1,46 +0,0 @@
|
||||||
import { last, flow } from "lodash/fp"
|
|
||||||
|
|
||||||
export const buildStyle = styles => {
|
|
||||||
let str = ""
|
|
||||||
for (let s in styles) {
|
|
||||||
if (styles[s]) {
|
|
||||||
let key = convertCamel(s)
|
|
||||||
str += `${key}: ${styles[s]}; `
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return str
|
|
||||||
}
|
|
||||||
|
|
||||||
export const convertCamel = str => {
|
|
||||||
return str.replace(/[A-Z]/g, match => `-${match.toLowerCase()}`)
|
|
||||||
}
|
|
||||||
|
|
||||||
export const pipe = (arg, funcs) => flow(funcs)(arg)
|
|
||||||
|
|
||||||
export const capitalise = s => {
|
|
||||||
if (!s) {
|
|
||||||
return s
|
|
||||||
}
|
|
||||||
return s.substring(0, 1).toUpperCase() + s.substring(1)
|
|
||||||
}
|
|
||||||
|
|
||||||
export const lowercase = s => s.substring(0, 1).toLowerCase() + s.substring(1)
|
|
||||||
|
|
||||||
export const lowercaseExceptFirst = s =>
|
|
||||||
s.charAt(0) + s.substring(1).toLowerCase()
|
|
||||||
|
|
||||||
export const get_name = s => (!s ? "" : last(s.split("/")))
|
|
||||||
|
|
||||||
export const get_capitalised_name = name => pipe(name, [get_name, capitalise])
|
|
||||||
|
|
||||||
export const isBuilderInputFocused = e => {
|
|
||||||
const activeTag = document.activeElement?.tagName.toLowerCase()
|
|
||||||
const inCodeEditor = document.activeElement?.classList?.contains("cm-content")
|
|
||||||
if (
|
|
||||||
(inCodeEditor || ["input", "textarea"].indexOf(activeTag) !== -1) &&
|
|
||||||
e.key !== "Escape"
|
|
||||||
) {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
|
@ -0,0 +1,50 @@
|
||||||
|
import type { Many } from "lodash"
|
||||||
|
import { last, flow } from "lodash/fp"
|
||||||
|
|
||||||
|
export const buildStyle = (styles: Record<string, any>) => {
|
||||||
|
let str = ""
|
||||||
|
for (let s in styles) {
|
||||||
|
if (styles[s]) {
|
||||||
|
let key = convertCamel(s)
|
||||||
|
str += `${key}: ${styles[s]}; `
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return str
|
||||||
|
}
|
||||||
|
|
||||||
|
export const convertCamel = (str: string) => {
|
||||||
|
return str.replace(/[A-Z]/g, match => `-${match.toLowerCase()}`)
|
||||||
|
}
|
||||||
|
|
||||||
|
export const pipe = (arg: string, funcs: Many<(...args: any[]) => any>) =>
|
||||||
|
flow(funcs)(arg)
|
||||||
|
|
||||||
|
export const capitalise = (s: string) => {
|
||||||
|
if (!s) {
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
return s.substring(0, 1).toUpperCase() + s.substring(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
export const lowercase = (s: string) =>
|
||||||
|
s.substring(0, 1).toLowerCase() + s.substring(1)
|
||||||
|
|
||||||
|
export const lowercaseExceptFirst = (s: string) =>
|
||||||
|
s.charAt(0) + s.substring(1).toLowerCase()
|
||||||
|
|
||||||
|
export const get_name = (s: string) => (!s ? "" : last(s.split("/")))
|
||||||
|
|
||||||
|
export const get_capitalised_name = (name: string) =>
|
||||||
|
pipe(name, [get_name, capitalise])
|
||||||
|
|
||||||
|
export const isBuilderInputFocused = (e: KeyboardEvent) => {
|
||||||
|
const activeTag = document.activeElement?.tagName.toLowerCase()
|
||||||
|
const inCodeEditor = document.activeElement?.classList?.contains("cm-content")
|
||||||
|
if (
|
||||||
|
(inCodeEditor || ["input", "textarea"].indexOf(activeTag!) !== -1) &&
|
||||||
|
e.key !== "Escape"
|
||||||
|
) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
|
@ -1,7 +0,0 @@
|
||||||
function handleEnter(fnc) {
|
|
||||||
return e => e.key === "Enter" && fnc()
|
|
||||||
}
|
|
||||||
|
|
||||||
export const keyUtils = {
|
|
||||||
handleEnter,
|
|
||||||
}
|
|
|
@ -0,0 +1,7 @@
|
||||||
|
function handleEnter(fnc: () => void) {
|
||||||
|
return (e: KeyboardEvent) => e.key === "Enter" && fnc()
|
||||||
|
}
|
||||||
|
|
||||||
|
export const keyUtils = {
|
||||||
|
handleEnter,
|
||||||
|
}
|
|
@ -1,6 +1,16 @@
|
||||||
import { writable } from "svelte/store"
|
import { writable } from "svelte/store"
|
||||||
|
|
||||||
function defaultValue() {
|
interface PaginationStore {
|
||||||
|
nextPage: string | null | undefined
|
||||||
|
page: string | null | undefined
|
||||||
|
hasPrevPage: boolean
|
||||||
|
hasNextPage: boolean
|
||||||
|
loading: boolean
|
||||||
|
pageNumber: number
|
||||||
|
pages: string[]
|
||||||
|
}
|
||||||
|
|
||||||
|
function defaultValue(): PaginationStore {
|
||||||
return {
|
return {
|
||||||
nextPage: null,
|
nextPage: null,
|
||||||
page: undefined,
|
page: undefined,
|
||||||
|
@ -29,13 +39,13 @@ export function createPaginationStore() {
|
||||||
update(state => {
|
update(state => {
|
||||||
state.pageNumber++
|
state.pageNumber++
|
||||||
state.page = state.nextPage
|
state.page = state.nextPage
|
||||||
state.pages.push(state.page)
|
state.pages.push(state.page!)
|
||||||
state.hasPrevPage = state.pageNumber > 1
|
state.hasPrevPage = state.pageNumber > 1
|
||||||
return state
|
return state
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
function fetched(hasNextPage, nextPage) {
|
function fetched(hasNextPage: boolean, nextPage: string) {
|
||||||
update(state => {
|
update(state => {
|
||||||
state.hasNextPage = hasNextPage
|
state.hasNextPage = hasNextPage
|
||||||
state.nextPage = nextPage
|
state.nextPage = nextPage
|
|
@ -1,6 +1,6 @@
|
||||||
import { PlanType } from "@budibase/types"
|
import { PlanType } from "@budibase/types"
|
||||||
|
|
||||||
export function getFormattedPlanName(userPlanType) {
|
export function getFormattedPlanName(userPlanType: PlanType) {
|
||||||
let planName
|
let planName
|
||||||
switch (userPlanType) {
|
switch (userPlanType) {
|
||||||
case PlanType.PRO:
|
case PlanType.PRO:
|
||||||
|
@ -29,6 +29,6 @@ export function getFormattedPlanName(userPlanType) {
|
||||||
return `${planName} Plan`
|
return `${planName} Plan`
|
||||||
}
|
}
|
||||||
|
|
||||||
export function isPremiumOrAbove(userPlanType) {
|
export function isPremiumOrAbove(userPlanType: PlanType) {
|
||||||
return ![PlanType.PRO, PlanType.TEAM, PlanType.FREE].includes(userPlanType)
|
return ![PlanType.PRO, PlanType.TEAM, PlanType.FREE].includes(userPlanType)
|
||||||
}
|
}
|
|
@ -1,4 +1,4 @@
|
||||||
export default function (url) {
|
export default function (url: string) {
|
||||||
return url
|
return url
|
||||||
.split("/")
|
.split("/")
|
||||||
.map(part => {
|
.map(part => {
|
|
@ -1,72 +0,0 @@
|
||||||
import { FieldType } from "@budibase/types"
|
|
||||||
import { ActionStepID } from "@/constants/backend/automations"
|
|
||||||
import { TableNames } from "@/constants"
|
|
||||||
import {
|
|
||||||
AUTO_COLUMN_DISPLAY_NAMES,
|
|
||||||
AUTO_COLUMN_SUB_TYPES,
|
|
||||||
FIELDS,
|
|
||||||
isAutoColumnUserRelationship,
|
|
||||||
} from "@/constants/backend"
|
|
||||||
|
|
||||||
export function getAutoColumnInformation(enabled = true) {
|
|
||||||
let info = {}
|
|
||||||
for (const [key, subtype] of Object.entries(AUTO_COLUMN_SUB_TYPES)) {
|
|
||||||
// Because it's possible to replicate the functionality of CREATED_AT and
|
|
||||||
// CREATED_BY columns with user column default values, we disable their creation
|
|
||||||
if (
|
|
||||||
subtype === AUTO_COLUMN_SUB_TYPES.CREATED_AT ||
|
|
||||||
subtype === AUTO_COLUMN_SUB_TYPES.CREATED_BY
|
|
||||||
) {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
info[subtype] = { enabled, name: AUTO_COLUMN_DISPLAY_NAMES[key] }
|
|
||||||
}
|
|
||||||
return info
|
|
||||||
}
|
|
||||||
|
|
||||||
export function buildAutoColumn(tableName, name, subtype) {
|
|
||||||
let type, constraints
|
|
||||||
switch (subtype) {
|
|
||||||
case AUTO_COLUMN_SUB_TYPES.UPDATED_BY:
|
|
||||||
case AUTO_COLUMN_SUB_TYPES.CREATED_BY:
|
|
||||||
type = FieldType.LINK
|
|
||||||
constraints = FIELDS.LINK.constraints
|
|
||||||
break
|
|
||||||
case AUTO_COLUMN_SUB_TYPES.AUTO_ID:
|
|
||||||
type = FieldType.NUMBER
|
|
||||||
constraints = FIELDS.NUMBER.constraints
|
|
||||||
break
|
|
||||||
case AUTO_COLUMN_SUB_TYPES.UPDATED_AT:
|
|
||||||
case AUTO_COLUMN_SUB_TYPES.CREATED_AT:
|
|
||||||
type = FieldType.DATETIME
|
|
||||||
constraints = FIELDS.DATETIME.constraints
|
|
||||||
break
|
|
||||||
default:
|
|
||||||
type = FieldType.STRING
|
|
||||||
constraints = FIELDS.STRING.constraints
|
|
||||||
break
|
|
||||||
}
|
|
||||||
if (Object.values(AUTO_COLUMN_SUB_TYPES).indexOf(subtype) === -1) {
|
|
||||||
throw "Cannot build auto column with supplied subtype"
|
|
||||||
}
|
|
||||||
const base = {
|
|
||||||
name,
|
|
||||||
type,
|
|
||||||
subtype,
|
|
||||||
icon: "ri-magic-line",
|
|
||||||
autocolumn: true,
|
|
||||||
constraints,
|
|
||||||
}
|
|
||||||
if (isAutoColumnUserRelationship(subtype)) {
|
|
||||||
base.tableId = TableNames.USERS
|
|
||||||
base.fieldName = `${tableName}-${name}`
|
|
||||||
}
|
|
||||||
return base
|
|
||||||
}
|
|
||||||
|
|
||||||
export function checkForCollectStep(automation) {
|
|
||||||
return automation.definition.steps.some(
|
|
||||||
step => step.stepId === ActionStepID.COLLECT
|
|
||||||
)
|
|
||||||
}
|
|
|
@ -0,0 +1,96 @@
|
||||||
|
import {
|
||||||
|
AutoFieldSubType,
|
||||||
|
Automation,
|
||||||
|
DateFieldMetadata,
|
||||||
|
FieldType,
|
||||||
|
NumberFieldMetadata,
|
||||||
|
RelationshipFieldMetadata,
|
||||||
|
RelationshipType,
|
||||||
|
} from "@budibase/types"
|
||||||
|
import { ActionStepID } from "@/constants/backend/automations"
|
||||||
|
import { TableNames } from "@/constants"
|
||||||
|
import {
|
||||||
|
AUTO_COLUMN_DISPLAY_NAMES,
|
||||||
|
AUTO_COLUMN_SUB_TYPES,
|
||||||
|
FIELDS,
|
||||||
|
} from "@/constants/backend"
|
||||||
|
import { utils } from "@budibase/shared-core"
|
||||||
|
|
||||||
|
type AutoColumnInformation = Partial<
|
||||||
|
Record<AutoFieldSubType, { enabled: boolean; name: string }>
|
||||||
|
>
|
||||||
|
|
||||||
|
export function getAutoColumnInformation(
|
||||||
|
enabled = true
|
||||||
|
): AutoColumnInformation {
|
||||||
|
const info: AutoColumnInformation = {}
|
||||||
|
for (const [key, subtype] of Object.entries(AUTO_COLUMN_SUB_TYPES)) {
|
||||||
|
// Because it's possible to replicate the functionality of CREATED_AT and
|
||||||
|
// CREATED_BY columns with user column default values, we disable their creation
|
||||||
|
if (
|
||||||
|
subtype === AUTO_COLUMN_SUB_TYPES.CREATED_AT ||
|
||||||
|
subtype === AUTO_COLUMN_SUB_TYPES.CREATED_BY
|
||||||
|
) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
const typedKey = key as keyof typeof AUTO_COLUMN_SUB_TYPES
|
||||||
|
info[subtype] = {
|
||||||
|
enabled,
|
||||||
|
name: AUTO_COLUMN_DISPLAY_NAMES[typedKey],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return info
|
||||||
|
}
|
||||||
|
|
||||||
|
export function buildAutoColumn(
|
||||||
|
tableName: string,
|
||||||
|
name: string,
|
||||||
|
subtype: AutoFieldSubType
|
||||||
|
): RelationshipFieldMetadata | NumberFieldMetadata | DateFieldMetadata {
|
||||||
|
const base = {
|
||||||
|
name,
|
||||||
|
icon: "ri-magic-line",
|
||||||
|
autocolumn: true,
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (subtype) {
|
||||||
|
case AUTO_COLUMN_SUB_TYPES.UPDATED_BY:
|
||||||
|
case AUTO_COLUMN_SUB_TYPES.CREATED_BY:
|
||||||
|
return {
|
||||||
|
...base,
|
||||||
|
type: FieldType.LINK,
|
||||||
|
subtype,
|
||||||
|
constraints: FIELDS.LINK.constraints,
|
||||||
|
tableId: TableNames.USERS,
|
||||||
|
fieldName: `${tableName}-${name}`,
|
||||||
|
relationshipType: RelationshipType.MANY_TO_ONE,
|
||||||
|
}
|
||||||
|
|
||||||
|
case AUTO_COLUMN_SUB_TYPES.AUTO_ID:
|
||||||
|
return {
|
||||||
|
...base,
|
||||||
|
type: FieldType.NUMBER,
|
||||||
|
subtype,
|
||||||
|
constraints: FIELDS.NUMBER.constraints,
|
||||||
|
}
|
||||||
|
case AUTO_COLUMN_SUB_TYPES.UPDATED_AT:
|
||||||
|
case AUTO_COLUMN_SUB_TYPES.CREATED_AT:
|
||||||
|
return {
|
||||||
|
...base,
|
||||||
|
type: FieldType.DATETIME,
|
||||||
|
subtype,
|
||||||
|
constraints: FIELDS.DATETIME.constraints,
|
||||||
|
}
|
||||||
|
|
||||||
|
default:
|
||||||
|
throw utils.unreachable(subtype, {
|
||||||
|
message: "Cannot build auto column with supplied subtype",
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function checkForCollectStep(automation: Automation) {
|
||||||
|
return automation.definition.steps.some(
|
||||||
|
step => step.stepId === ActionStepID.COLLECT
|
||||||
|
)
|
||||||
|
}
|
|
@ -1,4 +1,4 @@
|
||||||
export const suppressWarnings = warnings => {
|
export const suppressWarnings = (warnings: string[]) => {
|
||||||
if (!warnings?.length) {
|
if (!warnings?.length) {
|
||||||
return
|
return
|
||||||
}
|
}
|
|
@ -1,27 +0,0 @@
|
||||||
import { writable } from "svelte/store"
|
|
||||||
import { API } from "@/api"
|
|
||||||
|
|
||||||
export function createPermissionStore() {
|
|
||||||
const { subscribe } = writable([])
|
|
||||||
|
|
||||||
return {
|
|
||||||
subscribe,
|
|
||||||
save: async ({ level, role, resource }) => {
|
|
||||||
return await API.updatePermissionForResource(resource, role, level)
|
|
||||||
},
|
|
||||||
remove: async ({ level, role, resource }) => {
|
|
||||||
return await API.removePermissionFromResource(resource, role, level)
|
|
||||||
},
|
|
||||||
forResource: async resourceId => {
|
|
||||||
return (await API.getPermissionForResource(resourceId)).permissions
|
|
||||||
},
|
|
||||||
forResourceDetailed: async resourceId => {
|
|
||||||
return await API.getPermissionForResource(resourceId)
|
|
||||||
},
|
|
||||||
getDependantsInfo: async resourceId => {
|
|
||||||
return await API.getDependants(resourceId)
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export const permissions = createPermissionStore()
|
|
|
@ -0,0 +1,50 @@
|
||||||
|
import { BudiStore } from "../BudiStore"
|
||||||
|
import { API } from "@/api"
|
||||||
|
import {
|
||||||
|
PermissionLevel,
|
||||||
|
GetResourcePermsResponse,
|
||||||
|
GetDependantResourcesResponse,
|
||||||
|
ResourcePermissionInfo,
|
||||||
|
} from "@budibase/types"
|
||||||
|
|
||||||
|
interface Permission {
|
||||||
|
level: PermissionLevel
|
||||||
|
role: string
|
||||||
|
resource: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export class PermissionStore extends BudiStore<Permission[]> {
|
||||||
|
constructor() {
|
||||||
|
super([])
|
||||||
|
}
|
||||||
|
|
||||||
|
save = async (permission: Permission) => {
|
||||||
|
const { level, role, resource } = permission
|
||||||
|
return await API.updatePermissionForResource(resource, role, level)
|
||||||
|
}
|
||||||
|
|
||||||
|
remove = async (permission: Permission) => {
|
||||||
|
const { level, role, resource } = permission
|
||||||
|
return await API.removePermissionFromResource(resource, role, level)
|
||||||
|
}
|
||||||
|
|
||||||
|
forResource = async (
|
||||||
|
resourceId: string
|
||||||
|
): Promise<Record<string, ResourcePermissionInfo>> => {
|
||||||
|
return (await API.getPermissionForResource(resourceId)).permissions
|
||||||
|
}
|
||||||
|
|
||||||
|
forResourceDetailed = async (
|
||||||
|
resourceId: string
|
||||||
|
): Promise<GetResourcePermsResponse> => {
|
||||||
|
return await API.getPermissionForResource(resourceId)
|
||||||
|
}
|
||||||
|
|
||||||
|
getDependantsInfo = async (
|
||||||
|
resourceId: string
|
||||||
|
): Promise<GetDependantResourcesResponse> => {
|
||||||
|
return await API.getDependants(resourceId)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const permissions = new PermissionStore()
|
|
@ -62,7 +62,7 @@ export class RowActionStore extends BudiStore<RowActionState> {
|
||||||
const existingRowActions = get(this)[tableId] || []
|
const existingRowActions = get(this)[tableId] || []
|
||||||
name = getSequentialName(existingRowActions, "New row action ", {
|
name = getSequentialName(existingRowActions, "New row action ", {
|
||||||
getName: x => x.name,
|
getName: x => x.name,
|
||||||
})
|
})!
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!name) {
|
if (!name) {
|
||||||
|
|
|
@ -1,67 +0,0 @@
|
||||||
import { writable, derived } from "svelte/store"
|
|
||||||
import { tables } from "./tables"
|
|
||||||
import { API } from "@/api"
|
|
||||||
|
|
||||||
export function createViewsStore() {
|
|
||||||
const store = writable({
|
|
||||||
selectedViewName: null,
|
|
||||||
})
|
|
||||||
const derivedStore = derived([store, tables], ([$store, $tables]) => {
|
|
||||||
let list = []
|
|
||||||
$tables.list?.forEach(table => {
|
|
||||||
const views = Object.values(table?.views || {}).filter(view => {
|
|
||||||
return view.version !== 2
|
|
||||||
})
|
|
||||||
list = list.concat(views)
|
|
||||||
})
|
|
||||||
return {
|
|
||||||
...$store,
|
|
||||||
list,
|
|
||||||
selected: list.find(view => view.name === $store.selectedViewName),
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
const select = name => {
|
|
||||||
store.update(state => ({
|
|
||||||
...state,
|
|
||||||
selectedViewName: name,
|
|
||||||
}))
|
|
||||||
}
|
|
||||||
|
|
||||||
const deleteView = async view => {
|
|
||||||
await API.deleteView(view.name)
|
|
||||||
|
|
||||||
// Update tables
|
|
||||||
tables.update(state => {
|
|
||||||
const table = state.list.find(table => table._id === view.tableId)
|
|
||||||
delete table.views[view.name]
|
|
||||||
return { ...state }
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
const save = async view => {
|
|
||||||
const savedView = await API.saveView(view)
|
|
||||||
select(view.name)
|
|
||||||
|
|
||||||
// Update tables
|
|
||||||
tables.update(state => {
|
|
||||||
const table = state.list.find(table => table._id === view.tableId)
|
|
||||||
if (table) {
|
|
||||||
if (view.originalName) {
|
|
||||||
delete table.views[view.originalName]
|
|
||||||
}
|
|
||||||
table.views[view.name] = savedView
|
|
||||||
}
|
|
||||||
return { ...state }
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
subscribe: derivedStore.subscribe,
|
|
||||||
select,
|
|
||||||
delete: deleteView,
|
|
||||||
save,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export const views = createViewsStore()
|
|
|
@ -0,0 +1,94 @@
|
||||||
|
import { DerivedBudiStore } from "../BudiStore"
|
||||||
|
import { tables } from "./tables"
|
||||||
|
import { API } from "@/api"
|
||||||
|
import { View } from "@budibase/types"
|
||||||
|
import { helpers } from "@budibase/shared-core"
|
||||||
|
import { derived, Writable } from "svelte/store"
|
||||||
|
|
||||||
|
interface BuilderViewStore {
|
||||||
|
selectedViewName: string | null
|
||||||
|
}
|
||||||
|
|
||||||
|
interface DerivedViewStore extends BuilderViewStore {
|
||||||
|
list: View[]
|
||||||
|
selected?: View
|
||||||
|
}
|
||||||
|
|
||||||
|
export class ViewsStore extends DerivedBudiStore<
|
||||||
|
BuilderViewStore,
|
||||||
|
DerivedViewStore
|
||||||
|
> {
|
||||||
|
constructor() {
|
||||||
|
const makeDerivedStore = (store: Writable<BuilderViewStore>) => {
|
||||||
|
return derived([store, tables], ([$store, $tables]): DerivedViewStore => {
|
||||||
|
let list: View[] = []
|
||||||
|
$tables.list?.forEach(table => {
|
||||||
|
const views = Object.values(table?.views || {}).filter(
|
||||||
|
(view): view is View => !helpers.views.isV2(view)
|
||||||
|
)
|
||||||
|
list = list.concat(views)
|
||||||
|
})
|
||||||
|
return {
|
||||||
|
selectedViewName: $store.selectedViewName,
|
||||||
|
list,
|
||||||
|
selected: list.find(view => view.name === $store.selectedViewName),
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
super(
|
||||||
|
{
|
||||||
|
selectedViewName: null,
|
||||||
|
},
|
||||||
|
makeDerivedStore
|
||||||
|
)
|
||||||
|
|
||||||
|
this.select = this.select.bind(this)
|
||||||
|
}
|
||||||
|
|
||||||
|
select = (name: string) => {
|
||||||
|
this.store.update(state => ({
|
||||||
|
...state,
|
||||||
|
selectedViewName: name,
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
|
delete = async (view: View) => {
|
||||||
|
if (!view.name) {
|
||||||
|
throw new Error("View name is required")
|
||||||
|
}
|
||||||
|
await API.deleteView(view.name)
|
||||||
|
|
||||||
|
// Update tables
|
||||||
|
tables.update(state => {
|
||||||
|
const table = state.list.find(table => table._id === view.tableId)
|
||||||
|
if (table?.views && view.name) {
|
||||||
|
delete table.views[view.name]
|
||||||
|
}
|
||||||
|
return { ...state }
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
save = async (view: View & { originalName?: string }) => {
|
||||||
|
if (!view.name) {
|
||||||
|
throw new Error("View name is required")
|
||||||
|
}
|
||||||
|
|
||||||
|
const savedView = await API.saveView(view)
|
||||||
|
this.select(view.name)
|
||||||
|
|
||||||
|
// Update tables
|
||||||
|
tables.update(state => {
|
||||||
|
const table = state.list.find(table => table._id === view.tableId)
|
||||||
|
if (table?.views && view.name) {
|
||||||
|
if (view.originalName) {
|
||||||
|
delete table.views[view.originalName]
|
||||||
|
}
|
||||||
|
table.views[view.name] = savedView
|
||||||
|
}
|
||||||
|
return { ...state }
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const views = new ViewsStore()
|
|
@ -1 +1 @@
|
||||||
Subproject commit 193476cdfade6d3c613e6972f16ee0c527e01ff6
|
Subproject commit a4f63b22675e16dcdcaa4d9e83b298eee6466a07
|
Loading…
Reference in New Issue