Merge branch 'master' into BUDI-7580/account_portal_submodule
This commit is contained in:
commit
ef914882d4
|
@ -45,8 +45,8 @@ jobs:
|
||||||
BUMP_TYPE=${BUMP_TYPE_INPUT:-"patch"}
|
BUMP_TYPE=${BUMP_TYPE_INPUT:-"patch"}
|
||||||
./versionCommit.sh $BUMP_TYPE
|
./versionCommit.sh $BUMP_TYPE
|
||||||
|
|
||||||
|
cd ..
|
||||||
new_version=$(./getCurrentVersion.sh)
|
new_version=$(./scripts/getCurrentVersion.sh)
|
||||||
echo "version=$new_version" >> $GITHUB_OUTPUT
|
echo "version=$new_version" >> $GITHUB_OUTPUT
|
||||||
|
|
||||||
trigger-release:
|
trigger-release:
|
||||||
|
|
|
@ -2,16 +2,18 @@ server {
|
||||||
listen 443 ssl default_server;
|
listen 443 ssl default_server;
|
||||||
listen [::]:443 ssl default_server;
|
listen [::]:443 ssl default_server;
|
||||||
server_name _;
|
server_name _;
|
||||||
ssl_certificate /etc/letsencrypt/live/CUSTOM_DOMAIN/fullchain.pem;
|
error_log /dev/stderr warn;
|
||||||
ssl_certificate_key /etc/letsencrypt/live/CUSTOM_DOMAIN/privkey.pem;
|
access_log /dev/stdout main;
|
||||||
include /etc/letsencrypt/options-ssl-nginx.conf;
|
|
||||||
ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;
|
|
||||||
|
|
||||||
client_max_body_size 1000m;
|
client_max_body_size 1000m;
|
||||||
ignore_invalid_headers off;
|
ignore_invalid_headers off;
|
||||||
proxy_buffering off;
|
proxy_buffering off;
|
||||||
# port_in_redirect off;
|
# port_in_redirect off;
|
||||||
|
|
||||||
|
ssl_certificate /etc/letsencrypt/live/CUSTOM_DOMAIN/fullchain.pem;
|
||||||
|
ssl_certificate_key /etc/letsencrypt/live/CUSTOM_DOMAIN/privkey.pem;
|
||||||
|
include /etc/letsencrypt/options-ssl-nginx.conf;
|
||||||
|
ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;
|
||||||
|
|
||||||
location ^~ /.well-known/acme-challenge/ {
|
location ^~ /.well-known/acme-challenge/ {
|
||||||
default_type "text/plain";
|
default_type "text/plain";
|
||||||
root /var/www/html;
|
root /var/www/html;
|
||||||
|
@ -47,6 +49,24 @@ server {
|
||||||
rewrite ^/worker/(.*)$ /$1 break;
|
rewrite ^/worker/(.*)$ /$1 break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
location /api/backups/ {
|
||||||
|
# calls to export apps are limited
|
||||||
|
limit_req zone=ratelimit burst=20 nodelay;
|
||||||
|
|
||||||
|
# 1800s timeout for app export requests
|
||||||
|
proxy_read_timeout 1800s;
|
||||||
|
proxy_connect_timeout 1800s;
|
||||||
|
proxy_send_timeout 1800s;
|
||||||
|
|
||||||
|
proxy_http_version 1.1;
|
||||||
|
proxy_set_header Connection $connection_upgrade;
|
||||||
|
proxy_set_header Upgrade $http_upgrade;
|
||||||
|
proxy_set_header X-Real-IP $remote_addr;
|
||||||
|
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||||
|
|
||||||
|
proxy_pass http://127.0.0.1:4001;
|
||||||
|
}
|
||||||
|
|
||||||
location /api/ {
|
location /api/ {
|
||||||
# calls to the API are rate limited with bursting
|
# calls to the API are rate limited with bursting
|
||||||
limit_req zone=ratelimit burst=20 nodelay;
|
limit_req zone=ratelimit burst=20 nodelay;
|
||||||
|
@ -70,18 +90,49 @@ server {
|
||||||
rewrite ^/db/(.*)$ /$1 break;
|
rewrite ^/db/(.*)$ /$1 break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
location /socket/ {
|
||||||
|
proxy_http_version 1.1;
|
||||||
|
proxy_set_header Upgrade $http_upgrade;
|
||||||
|
proxy_set_header Connection 'upgrade';
|
||||||
|
proxy_set_header Host $host;
|
||||||
|
proxy_cache_bypass $http_upgrade;
|
||||||
|
proxy_pass http://127.0.0.1:4001;
|
||||||
|
}
|
||||||
|
|
||||||
location / {
|
location / {
|
||||||
proxy_set_header X-Real-IP $remote_addr;
|
proxy_set_header X-Real-IP $remote_addr;
|
||||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||||
proxy_set_header X-Forwarded-Proto $scheme;
|
proxy_set_header X-Forwarded-Proto $scheme;
|
||||||
|
proxy_set_header Host $http_host;
|
||||||
|
|
||||||
proxy_connect_timeout 300;
|
proxy_connect_timeout 300;
|
||||||
proxy_http_version 1.1;
|
proxy_http_version 1.1;
|
||||||
proxy_set_header Connection "";
|
proxy_set_header Connection "";
|
||||||
chunked_transfer_encoding off;
|
chunked_transfer_encoding off;
|
||||||
|
|
||||||
proxy_pass http://127.0.0.1:9000;
|
proxy_pass http://127.0.0.1:9000;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
location /files/signed/ {
|
||||||
|
proxy_set_header X-Real-IP $remote_addr;
|
||||||
|
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||||
|
proxy_set_header X-Forwarded-Proto $scheme;
|
||||||
|
|
||||||
|
# IMPORTANT: Signed urls will inspect the host header of the request.
|
||||||
|
# Normally a signed url will need to be generated with a specified client host in mind.
|
||||||
|
# To support dynamic hosts, e.g. some unknown self-hosted installation url,
|
||||||
|
# use a predefined host header. The host 'minio-service' is also used at the time of url signing.
|
||||||
|
proxy_set_header Host minio-service;
|
||||||
|
|
||||||
|
proxy_connect_timeout 300;
|
||||||
|
proxy_http_version 1.1;
|
||||||
|
proxy_set_header Connection "";
|
||||||
|
chunked_transfer_encoding off;
|
||||||
|
|
||||||
|
proxy_pass http://127.0.0.1:9000;
|
||||||
|
rewrite ^/files/signed/(.*)$ /$1 break;
|
||||||
|
}
|
||||||
|
|
||||||
client_header_timeout 60;
|
client_header_timeout 60;
|
||||||
client_body_timeout 60;
|
client_body_timeout 60;
|
||||||
keepalive_timeout 60;
|
keepalive_timeout 60;
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
{
|
{
|
||||||
"version": "2.13.0",
|
"version": "2.13.3",
|
||||||
"npmClient": "yarn",
|
"npmClient": "yarn",
|
||||||
"packages": [
|
"packages": [
|
||||||
"packages/*",
|
"packages/*",
|
||||||
|
|
|
@ -30,6 +30,7 @@ export * as timers from "./timers"
|
||||||
export { default as env } from "./environment"
|
export { default as env } from "./environment"
|
||||||
export * as blacklist from "./blacklist"
|
export * as blacklist from "./blacklist"
|
||||||
export * as docUpdates from "./docUpdates"
|
export * as docUpdates from "./docUpdates"
|
||||||
|
export * from "./utils/Duration"
|
||||||
export { SearchParams } from "./db"
|
export { SearchParams } from "./db"
|
||||||
// Add context to tenancy for backwards compatibility
|
// Add context to tenancy for backwards compatibility
|
||||||
// only do this for external usages to prevent internal
|
// only do this for external usages to prevent internal
|
||||||
|
|
|
@ -36,7 +36,7 @@ class InMemoryQueue {
|
||||||
* @param opts This is not used by the in memory queue as there is no real use
|
* @param opts This is not used by the in memory queue as there is no real use
|
||||||
* case when in memory, but is the same API as Bull
|
* case when in memory, but is the same API as Bull
|
||||||
*/
|
*/
|
||||||
constructor(name: string, opts = null) {
|
constructor(name: string, opts?: any) {
|
||||||
this._name = name
|
this._name = name
|
||||||
this._opts = opts
|
this._opts = opts
|
||||||
this._messages = []
|
this._messages = []
|
||||||
|
|
|
@ -2,11 +2,17 @@ import env from "../environment"
|
||||||
import { getRedisOptions } from "../redis/utils"
|
import { getRedisOptions } from "../redis/utils"
|
||||||
import { JobQueue } from "./constants"
|
import { JobQueue } from "./constants"
|
||||||
import InMemoryQueue from "./inMemoryQueue"
|
import InMemoryQueue from "./inMemoryQueue"
|
||||||
import BullQueue from "bull"
|
import BullQueue, { QueueOptions } from "bull"
|
||||||
import { addListeners, StalledFn } from "./listeners"
|
import { addListeners, StalledFn } from "./listeners"
|
||||||
|
import { Duration } from "../utils"
|
||||||
import * as timers from "../timers"
|
import * as timers from "../timers"
|
||||||
|
|
||||||
const CLEANUP_PERIOD_MS = 60 * 1000
|
// the queue lock is held for 5 minutes
|
||||||
|
const QUEUE_LOCK_MS = Duration.fromMinutes(5).toMs()
|
||||||
|
// queue lock is refreshed every 30 seconds
|
||||||
|
const QUEUE_LOCK_RENEW_INTERNAL_MS = Duration.fromSeconds(30).toMs()
|
||||||
|
// cleanup the queue every 60 seconds
|
||||||
|
const CLEANUP_PERIOD_MS = Duration.fromSeconds(60).toMs()
|
||||||
let QUEUES: BullQueue.Queue[] | InMemoryQueue[] = []
|
let QUEUES: BullQueue.Queue[] | InMemoryQueue[] = []
|
||||||
let cleanupInterval: NodeJS.Timeout
|
let cleanupInterval: NodeJS.Timeout
|
||||||
|
|
||||||
|
@ -20,8 +26,15 @@ export function createQueue<T>(
|
||||||
jobQueue: JobQueue,
|
jobQueue: JobQueue,
|
||||||
opts: { removeStalledCb?: StalledFn } = {}
|
opts: { removeStalledCb?: StalledFn } = {}
|
||||||
): BullQueue.Queue<T> {
|
): BullQueue.Queue<T> {
|
||||||
const { opts: redisOpts, redisProtocolUrl } = getRedisOptions()
|
const redisOpts = getRedisOptions()
|
||||||
const queueConfig: any = redisProtocolUrl || { redis: redisOpts }
|
const queueConfig: QueueOptions = {
|
||||||
|
redis: redisOpts,
|
||||||
|
settings: {
|
||||||
|
maxStalledCount: 0,
|
||||||
|
lockDuration: QUEUE_LOCK_MS,
|
||||||
|
lockRenewTime: QUEUE_LOCK_RENEW_INTERNAL_MS,
|
||||||
|
},
|
||||||
|
}
|
||||||
let queue: any
|
let queue: any
|
||||||
if (!env.isTest()) {
|
if (!env.isTest()) {
|
||||||
queue = new BullQueue(jobQueue, queueConfig)
|
queue = new BullQueue(jobQueue, queueConfig)
|
||||||
|
|
|
@ -16,6 +16,7 @@ import {
|
||||||
getRedisOptions,
|
getRedisOptions,
|
||||||
SEPARATOR,
|
SEPARATOR,
|
||||||
SelectableDatabase,
|
SelectableDatabase,
|
||||||
|
getRedisConnectionDetails,
|
||||||
} from "./utils"
|
} from "./utils"
|
||||||
import * as timers from "../timers"
|
import * as timers from "../timers"
|
||||||
|
|
||||||
|
@ -91,12 +92,11 @@ function init(selectDb = DEFAULT_SELECT_DB) {
|
||||||
if (client) {
|
if (client) {
|
||||||
client.disconnect()
|
client.disconnect()
|
||||||
}
|
}
|
||||||
const { redisProtocolUrl, opts, host, port } = getRedisOptions()
|
const { host, port } = getRedisConnectionDetails()
|
||||||
|
const opts = getRedisOptions()
|
||||||
|
|
||||||
if (CLUSTERED) {
|
if (CLUSTERED) {
|
||||||
client = new RedisCore.Cluster([{ host, port }], opts)
|
client = new RedisCore.Cluster([{ host, port }], opts)
|
||||||
} else if (redisProtocolUrl) {
|
|
||||||
client = new RedisCore(redisProtocolUrl)
|
|
||||||
} else {
|
} else {
|
||||||
client = new RedisCore(opts)
|
client = new RedisCore(opts)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import env from "../environment"
|
import env from "../environment"
|
||||||
|
import * as Redis from "ioredis"
|
||||||
|
|
||||||
const SLOT_REFRESH_MS = 2000
|
const SLOT_REFRESH_MS = 2000
|
||||||
const CONNECT_TIMEOUT_MS = 10000
|
const CONNECT_TIMEOUT_MS = 10000
|
||||||
|
@ -42,7 +43,7 @@ export enum Databases {
|
||||||
export enum SelectableDatabase {
|
export enum SelectableDatabase {
|
||||||
DEFAULT = 0,
|
DEFAULT = 0,
|
||||||
SOCKET_IO = 1,
|
SOCKET_IO = 1,
|
||||||
UNUSED_1 = 2,
|
RATE_LIMITING = 2,
|
||||||
UNUSED_2 = 3,
|
UNUSED_2 = 3,
|
||||||
UNUSED_3 = 4,
|
UNUSED_3 = 4,
|
||||||
UNUSED_4 = 5,
|
UNUSED_4 = 5,
|
||||||
|
@ -58,7 +59,7 @@ export enum SelectableDatabase {
|
||||||
UNUSED_14 = 15,
|
UNUSED_14 = 15,
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getRedisOptions() {
|
export function getRedisConnectionDetails() {
|
||||||
let password = env.REDIS_PASSWORD
|
let password = env.REDIS_PASSWORD
|
||||||
let url: string[] | string = env.REDIS_URL.split("//")
|
let url: string[] | string = env.REDIS_URL.split("//")
|
||||||
// get rid of the protocol
|
// get rid of the protocol
|
||||||
|
@ -74,28 +75,34 @@ export function getRedisOptions() {
|
||||||
}
|
}
|
||||||
const [host, port] = url.split(":")
|
const [host, port] = url.split(":")
|
||||||
|
|
||||||
let redisProtocolUrl
|
return {
|
||||||
|
host,
|
||||||
// fully qualified redis URL
|
password,
|
||||||
if (/rediss?:\/\//.test(env.REDIS_URL)) {
|
port: parseInt(port),
|
||||||
redisProtocolUrl = env.REDIS_URL
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const opts: any = {
|
export function getRedisOptions() {
|
||||||
|
const { host, password, port } = getRedisConnectionDetails()
|
||||||
|
let redisOpts: Redis.RedisOptions = {
|
||||||
connectTimeout: CONNECT_TIMEOUT_MS,
|
connectTimeout: CONNECT_TIMEOUT_MS,
|
||||||
|
port: port,
|
||||||
|
host,
|
||||||
|
password,
|
||||||
}
|
}
|
||||||
|
let opts: Redis.ClusterOptions | Redis.RedisOptions = redisOpts
|
||||||
if (env.REDIS_CLUSTERED) {
|
if (env.REDIS_CLUSTERED) {
|
||||||
opts.redisOptions = {}
|
opts = {
|
||||||
opts.redisOptions.tls = {}
|
connectTimeout: CONNECT_TIMEOUT_MS,
|
||||||
opts.redisOptions.password = password
|
redisOptions: {
|
||||||
opts.slotsRefreshTimeout = SLOT_REFRESH_MS
|
...redisOpts,
|
||||||
opts.dnsLookup = (address: string, callback: any) => callback(null, address)
|
tls: {},
|
||||||
} else {
|
},
|
||||||
opts.host = host
|
slotsRefreshTimeout: SLOT_REFRESH_MS,
|
||||||
opts.port = port
|
dnsLookup: (address: string, callback: any) => callback(null, address),
|
||||||
opts.password = password
|
} as Redis.ClusterOptions
|
||||||
}
|
}
|
||||||
return { opts, host, port: parseInt(port), redisProtocolUrl }
|
return opts
|
||||||
}
|
}
|
||||||
|
|
||||||
export function addDbPrefix(db: string, key: string) {
|
export function addDbPrefix(db: string, key: string) {
|
||||||
|
|
|
@ -0,0 +1,49 @@
|
||||||
|
export enum DurationType {
|
||||||
|
MILLISECONDS = "milliseconds",
|
||||||
|
SECONDS = "seconds",
|
||||||
|
MINUTES = "minutes",
|
||||||
|
HOURS = "hours",
|
||||||
|
DAYS = "days",
|
||||||
|
}
|
||||||
|
|
||||||
|
const conversion: Record<DurationType, number> = {
|
||||||
|
milliseconds: 1,
|
||||||
|
seconds: 1000,
|
||||||
|
minutes: 60 * 1000,
|
||||||
|
hours: 60 * 60 * 1000,
|
||||||
|
days: 24 * 60 * 60 * 1000,
|
||||||
|
}
|
||||||
|
|
||||||
|
export class Duration {
|
||||||
|
static convert(from: DurationType, to: DurationType, duration: number) {
|
||||||
|
const milliseconds = duration * conversion[from]
|
||||||
|
return milliseconds / conversion[to]
|
||||||
|
}
|
||||||
|
|
||||||
|
static from(from: DurationType, duration: number) {
|
||||||
|
return {
|
||||||
|
to: (to: DurationType) => {
|
||||||
|
return Duration.convert(from, to, duration)
|
||||||
|
},
|
||||||
|
toMs: () => {
|
||||||
|
return Duration.convert(from, DurationType.MILLISECONDS, duration)
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static fromSeconds(duration: number) {
|
||||||
|
return Duration.from(DurationType.SECONDS, duration)
|
||||||
|
}
|
||||||
|
|
||||||
|
static fromMinutes(duration: number) {
|
||||||
|
return Duration.from(DurationType.MINUTES, duration)
|
||||||
|
}
|
||||||
|
|
||||||
|
static fromHours(duration: number) {
|
||||||
|
return Duration.from(DurationType.HOURS, duration)
|
||||||
|
}
|
||||||
|
|
||||||
|
static fromDays(duration: number) {
|
||||||
|
return Duration.from(DurationType.DAYS, duration)
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,3 +1,4 @@
|
||||||
export * from "./hashing"
|
export * from "./hashing"
|
||||||
export * from "./utils"
|
export * from "./utils"
|
||||||
export * from "./stringUtils"
|
export * from "./stringUtils"
|
||||||
|
export * from "./Duration"
|
||||||
|
|
|
@ -0,0 +1,19 @@
|
||||||
|
import { Duration, DurationType } from "../Duration"
|
||||||
|
|
||||||
|
describe("duration", () => {
|
||||||
|
it("should convert minutes to milliseconds", () => {
|
||||||
|
expect(Duration.fromMinutes(5).toMs()).toBe(300000)
|
||||||
|
})
|
||||||
|
|
||||||
|
it("should convert seconds to milliseconds", () => {
|
||||||
|
expect(Duration.fromSeconds(30).toMs()).toBe(30000)
|
||||||
|
})
|
||||||
|
|
||||||
|
it("should convert days to milliseconds", () => {
|
||||||
|
expect(Duration.fromDays(1).toMs()).toBe(86400000)
|
||||||
|
})
|
||||||
|
|
||||||
|
it("should convert minutes to days", () => {
|
||||||
|
expect(Duration.fromMinutes(1440).to(DurationType.DAYS)).toBe(1)
|
||||||
|
})
|
||||||
|
})
|
|
@ -8,6 +8,7 @@
|
||||||
export let id = null
|
export let id = null
|
||||||
export let text = null
|
export let text = null
|
||||||
export let disabled = false
|
export let disabled = false
|
||||||
|
export let readonly = false
|
||||||
export let size
|
export let size
|
||||||
export let indeterminate = false
|
export let indeterminate = false
|
||||||
|
|
||||||
|
@ -24,6 +25,7 @@
|
||||||
class:is-invalid={!!error}
|
class:is-invalid={!!error}
|
||||||
class:checked={value}
|
class:checked={value}
|
||||||
class:is-indeterminate={indeterminate}
|
class:is-indeterminate={indeterminate}
|
||||||
|
class:readonly
|
||||||
>
|
>
|
||||||
<input
|
<input
|
||||||
checked={value}
|
checked={value}
|
||||||
|
@ -68,4 +70,7 @@
|
||||||
.spectrum-Checkbox-input {
|
.spectrum-Checkbox-input {
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
}
|
}
|
||||||
|
.readonly {
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
@ -8,6 +8,7 @@
|
||||||
export let options = []
|
export let options = []
|
||||||
export let error = null
|
export let error = null
|
||||||
export let disabled = false
|
export let disabled = false
|
||||||
|
export let readonly = false
|
||||||
export let getOptionLabel = option => option
|
export let getOptionLabel = option => option
|
||||||
export let getOptionValue = option => option
|
export let getOptionValue = option => option
|
||||||
|
|
||||||
|
@ -34,6 +35,7 @@
|
||||||
title={getOptionLabel(option)}
|
title={getOptionLabel(option)}
|
||||||
class="spectrum-Checkbox spectrum-FieldGroup-item"
|
class="spectrum-Checkbox spectrum-FieldGroup-item"
|
||||||
class:is-invalid={!!error}
|
class:is-invalid={!!error}
|
||||||
|
class:readonly
|
||||||
>
|
>
|
||||||
<label
|
<label
|
||||||
class="spectrum-Checkbox spectrum-Checkbox--sizeM spectrum-FieldGroup-item"
|
class="spectrum-Checkbox spectrum-Checkbox--sizeM spectrum-FieldGroup-item"
|
||||||
|
@ -66,4 +68,7 @@
|
||||||
.spectrum-Checkbox-input {
|
.spectrum-Checkbox-input {
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
}
|
}
|
||||||
|
.readonly {
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
@ -9,6 +9,7 @@
|
||||||
|
|
||||||
export let id = null
|
export let id = null
|
||||||
export let disabled = false
|
export let disabled = false
|
||||||
|
export let readonly = false
|
||||||
export let error = null
|
export let error = null
|
||||||
export let enableTime = true
|
export let enableTime = true
|
||||||
export let value = null
|
export let value = null
|
||||||
|
@ -186,7 +187,7 @@
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
id={flatpickrId}
|
id={flatpickrId}
|
||||||
class:is-disabled={disabled}
|
class:is-disabled={disabled || readonly}
|
||||||
class:is-invalid={!!error}
|
class:is-invalid={!!error}
|
||||||
class="flatpickr spectrum-InputGroup spectrum-Datepicker"
|
class="flatpickr spectrum-InputGroup spectrum-Datepicker"
|
||||||
class:is-focused={open}
|
class:is-focused={open}
|
||||||
|
@ -211,6 +212,7 @@
|
||||||
{/if}
|
{/if}
|
||||||
<input
|
<input
|
||||||
{disabled}
|
{disabled}
|
||||||
|
{readonly}
|
||||||
data-input
|
data-input
|
||||||
type="text"
|
type="text"
|
||||||
class="spectrum-Textfield-input spectrum-InputGroup-input"
|
class="spectrum-Textfield-input spectrum-InputGroup-input"
|
||||||
|
|
|
@ -386,7 +386,7 @@
|
||||||
}
|
}
|
||||||
.compact .placeholder,
|
.compact .placeholder,
|
||||||
.compact img {
|
.compact img {
|
||||||
margin: 10px 16px;
|
margin: 8px 16px;
|
||||||
}
|
}
|
||||||
.compact img {
|
.compact img {
|
||||||
height: 90px;
|
height: 90px;
|
||||||
|
@ -456,6 +456,12 @@
|
||||||
color: var(--red);
|
color: var(--red);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.spectrum-Dropzone {
|
||||||
|
height: 220px;
|
||||||
|
}
|
||||||
|
.compact .spectrum-Dropzone {
|
||||||
|
height: 40px;
|
||||||
|
}
|
||||||
.spectrum-Dropzone.disabled {
|
.spectrum-Dropzone.disabled {
|
||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
background-color: var(--spectrum-global-color-gray-200);
|
background-color: var(--spectrum-global-color-gray-200);
|
||||||
|
@ -463,10 +469,6 @@
|
||||||
.disabled .spectrum-Heading--sizeL {
|
.disabled .spectrum-Heading--sizeL {
|
||||||
color: var(--spectrum-alias-text-color-disabled);
|
color: var(--spectrum-alias-text-color-disabled);
|
||||||
}
|
}
|
||||||
.compact .spectrum-Dropzone {
|
|
||||||
padding-top: 8px;
|
|
||||||
padding-bottom: 8px;
|
|
||||||
}
|
|
||||||
.compact .spectrum-IllustratedMessage-description {
|
.compact .spectrum-IllustratedMessage-description {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
}
|
}
|
||||||
|
@ -477,7 +479,6 @@
|
||||||
flex-wrap: wrap;
|
flex-wrap: wrap;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
.tag {
|
.tag {
|
||||||
margin-top: 8px;
|
margin-top: 8px;
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,6 +8,7 @@
|
||||||
export let options = []
|
export let options = []
|
||||||
export let error = null
|
export let error = null
|
||||||
export let disabled = false
|
export let disabled = false
|
||||||
|
export let readonly = false
|
||||||
export let getOptionLabel = option => option
|
export let getOptionLabel = option => option
|
||||||
export let getOptionValue = option => option
|
export let getOptionValue = option => option
|
||||||
export let getOptionTitle = option => option
|
export let getOptionTitle = option => option
|
||||||
|
@ -40,6 +41,7 @@
|
||||||
title={getOptionTitle(option)}
|
title={getOptionTitle(option)}
|
||||||
class="spectrum-Radio spectrum-FieldGroup-item spectrum-Radio--emphasized"
|
class="spectrum-Radio spectrum-FieldGroup-item spectrum-Radio--emphasized"
|
||||||
class:is-invalid={!!error}
|
class:is-invalid={!!error}
|
||||||
|
class:readonly
|
||||||
>
|
>
|
||||||
<input
|
<input
|
||||||
on:change={onChange}
|
on:change={onChange}
|
||||||
|
@ -62,4 +64,7 @@
|
||||||
.spectrum-Radio-input {
|
.spectrum-Radio-input {
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
}
|
}
|
||||||
|
.readonly {
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
@ -4,6 +4,7 @@
|
||||||
export let value = ""
|
export let value = ""
|
||||||
export let placeholder = null
|
export let placeholder = null
|
||||||
export let disabled = false
|
export let disabled = false
|
||||||
|
export let readonly = false
|
||||||
export let error = null
|
export let error = null
|
||||||
export let height = null
|
export let height = null
|
||||||
export let id = null
|
export let id = null
|
||||||
|
@ -20,6 +21,7 @@
|
||||||
{fullScreenOffset}
|
{fullScreenOffset}
|
||||||
{disabled}
|
{disabled}
|
||||||
{easyMDEOptions}
|
{easyMDEOptions}
|
||||||
|
{readonly}
|
||||||
on:change
|
on:change
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -5,6 +5,7 @@
|
||||||
export let value = ""
|
export let value = ""
|
||||||
export let placeholder = null
|
export let placeholder = null
|
||||||
export let disabled = false
|
export let disabled = false
|
||||||
|
export let readonly = false
|
||||||
export let error = null
|
export let error = null
|
||||||
export let id = null
|
export let id = null
|
||||||
export let height = null
|
export let height = null
|
||||||
|
@ -61,6 +62,7 @@
|
||||||
class="spectrum-Textfield-input"
|
class="spectrum-Textfield-input"
|
||||||
style={align ? `text-align: ${align}` : ""}
|
style={align ? `text-align: ${align}` : ""}
|
||||||
{disabled}
|
{disabled}
|
||||||
|
{readonly}
|
||||||
{id}
|
{id}
|
||||||
on:focus={() => (focus = true)}
|
on:focus={() => (focus = true)}
|
||||||
on:blur={onChange}
|
on:blur={onChange}
|
||||||
|
|
|
@ -7,6 +7,7 @@
|
||||||
export let label = null
|
export let label = null
|
||||||
export let labelPosition = "above"
|
export let labelPosition = "above"
|
||||||
export let disabled = false
|
export let disabled = false
|
||||||
|
export let readonly = false
|
||||||
export let error = null
|
export let error = null
|
||||||
export let enableTime = true
|
export let enableTime = true
|
||||||
export let timeOnly = false
|
export let timeOnly = false
|
||||||
|
@ -33,6 +34,7 @@
|
||||||
<DatePicker
|
<DatePicker
|
||||||
{error}
|
{error}
|
||||||
{disabled}
|
{disabled}
|
||||||
|
{readonly}
|
||||||
{value}
|
{value}
|
||||||
{placeholder}
|
{placeholder}
|
||||||
{enableTime}
|
{enableTime}
|
||||||
|
|
|
@ -8,6 +8,7 @@
|
||||||
export let id = null
|
export let id = null
|
||||||
export let fullScreenOffset = 0
|
export let fullScreenOffset = 0
|
||||||
export let disabled = false
|
export let disabled = false
|
||||||
|
export let readonly = false
|
||||||
export let easyMDEOptions
|
export let easyMDEOptions
|
||||||
|
|
||||||
const dispatch = createEventDispatcher()
|
const dispatch = createEventDispatcher()
|
||||||
|
@ -19,6 +20,9 @@
|
||||||
// control
|
// control
|
||||||
$: checkValue(value)
|
$: checkValue(value)
|
||||||
$: mde?.codemirror.on("change", debouncedUpdate)
|
$: mde?.codemirror.on("change", debouncedUpdate)
|
||||||
|
$: if (readonly || disabled) {
|
||||||
|
mde?.togglePreview()
|
||||||
|
}
|
||||||
|
|
||||||
const checkValue = val => {
|
const checkValue = val => {
|
||||||
if (mde && val !== latestValue) {
|
if (mde && val !== latestValue) {
|
||||||
|
@ -54,6 +58,7 @@
|
||||||
easyMDEOptions={{
|
easyMDEOptions={{
|
||||||
initialValue: value,
|
initialValue: value,
|
||||||
placeholder,
|
placeholder,
|
||||||
|
toolbar: disabled || readonly ? false : undefined,
|
||||||
...easyMDEOptions,
|
...easyMDEOptions,
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
|
|
|
@ -44,6 +44,8 @@
|
||||||
const NUMBER_TYPE = FIELDS.NUMBER.type
|
const NUMBER_TYPE = FIELDS.NUMBER.type
|
||||||
const JSON_TYPE = FIELDS.JSON.type
|
const JSON_TYPE = FIELDS.JSON.type
|
||||||
const DATE_TYPE = FIELDS.DATETIME.type
|
const DATE_TYPE = FIELDS.DATETIME.type
|
||||||
|
const USER_TYPE = FIELDS.USER.subtype
|
||||||
|
const USERS_TYPE = FIELDS.USERS.subtype
|
||||||
|
|
||||||
const dispatch = createEventDispatcher()
|
const dispatch = createEventDispatcher()
|
||||||
const PROHIBITED_COLUMN_NAMES = ["type", "_id", "_rev", "tableId"]
|
const PROHIBITED_COLUMN_NAMES = ["type", "_id", "_rev", "tableId"]
|
||||||
|
@ -287,6 +289,14 @@
|
||||||
if (saveColumn.type !== LINK_TYPE) {
|
if (saveColumn.type !== LINK_TYPE) {
|
||||||
delete saveColumn.fieldName
|
delete saveColumn.fieldName
|
||||||
}
|
}
|
||||||
|
if (isUsersColumn(saveColumn)) {
|
||||||
|
if (saveColumn.subtype === USER_TYPE) {
|
||||||
|
saveColumn.relationshipType = RelationshipType.ONE_TO_MANY
|
||||||
|
} else if (saveColumn.subtype === USERS_TYPE) {
|
||||||
|
saveColumn.relationshipType = RelationshipType.MANY_TO_MANY
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await tables.saveField({
|
await tables.saveField({
|
||||||
originalName,
|
originalName,
|
||||||
|
|
|
@ -20,7 +20,7 @@
|
||||||
let open = false
|
let open = false
|
||||||
|
|
||||||
// Auto hide the component when another item is selected
|
// Auto hide the component when another item is selected
|
||||||
$: if (open && $draggable.selected != componentInstance._id) {
|
$: if (open && $draggable.selected !== componentInstance._id) {
|
||||||
popover.hide()
|
popover.hide()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -100,13 +100,13 @@
|
||||||
}}
|
}}
|
||||||
on:close={() => {
|
on:close={() => {
|
||||||
open = false
|
open = false
|
||||||
if ($draggable.selected == componentInstance._id) {
|
if ($draggable.selected === componentInstance._id) {
|
||||||
$draggable.actions.select()
|
$draggable.actions.select()
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
{anchor}
|
{anchor}
|
||||||
align="left-outside"
|
align="left-outside"
|
||||||
showPopover={drawers.length == 0}
|
showPopover={drawers.length === 0}
|
||||||
clickOutsideOverride={drawers.length > 0}
|
clickOutsideOverride={drawers.length > 0}
|
||||||
maxHeight={600}
|
maxHeight={600}
|
||||||
handlePostionUpdate={customPositionHandler}
|
handlePostionUpdate={customPositionHandler}
|
||||||
|
@ -115,6 +115,7 @@
|
||||||
<Layout noPadding noGap>
|
<Layout noPadding noGap>
|
||||||
<slot name="header" />
|
<slot name="header" />
|
||||||
<ComponentSettingsSection
|
<ComponentSettingsSection
|
||||||
|
includeHidden
|
||||||
{componentInstance}
|
{componentInstance}
|
||||||
componentDefinition={parsedComponentDef}
|
componentDefinition={parsedComponentDef}
|
||||||
isScreen={false}
|
isScreen={false}
|
||||||
|
|
|
@ -112,9 +112,9 @@
|
||||||
}
|
}
|
||||||
await usersFetch.update({
|
await usersFetch.update({
|
||||||
query: {
|
query: {
|
||||||
appId: query || !filterByAppAccess ? null : prodAppId,
|
string: { email: query },
|
||||||
email: query,
|
|
||||||
},
|
},
|
||||||
|
appId: query || !filterByAppAccess ? null : prodAppId,
|
||||||
limit: 50,
|
limit: 50,
|
||||||
paginate: query || !filterByAppAccess ? null : false,
|
paginate: query || !filterByAppAccess ? null : false,
|
||||||
})
|
})
|
||||||
|
|
|
@ -16,16 +16,18 @@
|
||||||
export let isScreen = false
|
export let isScreen = false
|
||||||
export let onUpdateSetting
|
export let onUpdateSetting
|
||||||
export let showSectionTitle = true
|
export let showSectionTitle = true
|
||||||
|
export let includeHidden = false
|
||||||
export let tag
|
export let tag
|
||||||
|
|
||||||
$: sections = getSections(
|
$: sections = getSections(
|
||||||
componentInstance,
|
componentInstance,
|
||||||
componentDefinition,
|
componentDefinition,
|
||||||
isScreen,
|
isScreen,
|
||||||
tag
|
tag,
|
||||||
|
includeHidden
|
||||||
)
|
)
|
||||||
|
|
||||||
const getSections = (instance, definition, isScreen, tag) => {
|
const getSections = (instance, definition, isScreen, tag, includeHidden) => {
|
||||||
const settings = definition?.settings ?? []
|
const settings = definition?.settings ?? []
|
||||||
const generalSettings = settings.filter(
|
const generalSettings = settings.filter(
|
||||||
setting => !setting.section && setting.tag === tag
|
setting => !setting.section && setting.tag === tag
|
||||||
|
@ -52,7 +54,12 @@
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
section.settings.forEach(setting => {
|
section.settings.forEach(setting => {
|
||||||
setting.visible = canRenderControl(instance, setting, isScreen)
|
setting.visible = canRenderControl(
|
||||||
|
instance,
|
||||||
|
setting,
|
||||||
|
isScreen,
|
||||||
|
includeHidden
|
||||||
|
)
|
||||||
})
|
})
|
||||||
section.visible =
|
section.visible =
|
||||||
section.name === "General" ||
|
section.name === "General" ||
|
||||||
|
@ -122,16 +129,20 @@
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
const canRenderControl = (instance, setting, isScreen) => {
|
const canRenderControl = (instance, setting, isScreen, includeHidden) => {
|
||||||
// Prevent rendering on click setting for screens
|
// Prevent rendering on click setting for screens
|
||||||
if (setting?.type === "event" && isScreen) {
|
if (setting?.type === "event" && isScreen) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
// Check we have a component to render for this setting
|
||||||
const control = getComponentForSetting(setting)
|
const control = getComponentForSetting(setting)
|
||||||
if (!control) {
|
if (!control) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
// Check if setting is hidden
|
||||||
|
if (setting.hidden && !includeHidden) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
return shouldDisplay(instance, setting)
|
return shouldDisplay(instance, setting)
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -2589,6 +2589,17 @@
|
||||||
"key": "disabled",
|
"key": "disabled",
|
||||||
"defaultValue": false
|
"defaultValue": false
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"type": "boolean",
|
||||||
|
"label": "Read only",
|
||||||
|
"key": "readonly",
|
||||||
|
"defaultValue": false,
|
||||||
|
"dependsOn": {
|
||||||
|
"setting": "disabled",
|
||||||
|
"value": true,
|
||||||
|
"invert": true
|
||||||
|
}
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"type": "text",
|
"type": "text",
|
||||||
"label": "Initial form step",
|
"label": "Initial form step",
|
||||||
|
@ -2738,6 +2749,17 @@
|
||||||
"key": "disabled",
|
"key": "disabled",
|
||||||
"defaultValue": false
|
"defaultValue": false
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"type": "boolean",
|
||||||
|
"label": "Read only",
|
||||||
|
"key": "readonly",
|
||||||
|
"defaultValue": false,
|
||||||
|
"dependsOn": {
|
||||||
|
"setting": "disabled",
|
||||||
|
"value": true,
|
||||||
|
"invert": true
|
||||||
|
}
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"type": "validation/string",
|
"type": "validation/string",
|
||||||
"label": "Validation",
|
"label": "Validation",
|
||||||
|
@ -2776,6 +2798,35 @@
|
||||||
"barTitle": "Justify text"
|
"barTitle": "Justify text"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "select",
|
||||||
|
"label": "Layout",
|
||||||
|
"key": "span",
|
||||||
|
"defaultValue": 6,
|
||||||
|
"hidden": true,
|
||||||
|
"showInBar": true,
|
||||||
|
"barStyle": "buttons",
|
||||||
|
"options": [
|
||||||
|
{
|
||||||
|
"label": "1 column",
|
||||||
|
"value": 6,
|
||||||
|
"barIcon": "Stop",
|
||||||
|
"barTitle": "1 column"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "2 columns",
|
||||||
|
"value": 3,
|
||||||
|
"barIcon": "ColumnTwoA",
|
||||||
|
"barTitle": "2 columns"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "3 columns",
|
||||||
|
"value": 2,
|
||||||
|
"barIcon": "ViewColumn",
|
||||||
|
"barTitle": "3 columns"
|
||||||
|
}
|
||||||
|
]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
@ -2829,10 +2880,50 @@
|
||||||
"key": "disabled",
|
"key": "disabled",
|
||||||
"defaultValue": false
|
"defaultValue": false
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"type": "boolean",
|
||||||
|
"label": "Read only",
|
||||||
|
"key": "readonly",
|
||||||
|
"defaultValue": false,
|
||||||
|
"dependsOn": {
|
||||||
|
"setting": "disabled",
|
||||||
|
"value": true,
|
||||||
|
"invert": true
|
||||||
|
}
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"type": "validation/number",
|
"type": "validation/number",
|
||||||
"label": "Validation",
|
"label": "Validation",
|
||||||
"key": "validation"
|
"key": "validation"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "select",
|
||||||
|
"label": "Layout",
|
||||||
|
"key": "span",
|
||||||
|
"defaultValue": 6,
|
||||||
|
"hidden": true,
|
||||||
|
"showInBar": true,
|
||||||
|
"barStyle": "buttons",
|
||||||
|
"options": [
|
||||||
|
{
|
||||||
|
"label": "1 column",
|
||||||
|
"value": 6,
|
||||||
|
"barIcon": "Stop",
|
||||||
|
"barTitle": "1 column"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "2 columns",
|
||||||
|
"value": 3,
|
||||||
|
"barIcon": "ColumnTwoA",
|
||||||
|
"barTitle": "2 columns"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "3 columns",
|
||||||
|
"value": 2,
|
||||||
|
"barIcon": "ViewColumn",
|
||||||
|
"barTitle": "3 columns"
|
||||||
|
}
|
||||||
|
]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
@ -2885,6 +2976,35 @@
|
||||||
"label": "Disabled",
|
"label": "Disabled",
|
||||||
"key": "disabled",
|
"key": "disabled",
|
||||||
"defaultValue": false
|
"defaultValue": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "select",
|
||||||
|
"label": "Layout",
|
||||||
|
"key": "span",
|
||||||
|
"defaultValue": 6,
|
||||||
|
"hidden": true,
|
||||||
|
"showInBar": true,
|
||||||
|
"barStyle": "buttons",
|
||||||
|
"options": [
|
||||||
|
{
|
||||||
|
"label": "1 column",
|
||||||
|
"value": 6,
|
||||||
|
"barIcon": "Stop",
|
||||||
|
"barTitle": "1 column"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "2 columns",
|
||||||
|
"value": 3,
|
||||||
|
"barIcon": "ColumnTwoA",
|
||||||
|
"barTitle": "2 columns"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "3 columns",
|
||||||
|
"value": 2,
|
||||||
|
"barIcon": "ViewColumn",
|
||||||
|
"barTitle": "3 columns"
|
||||||
|
}
|
||||||
|
]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
@ -2942,6 +3062,35 @@
|
||||||
"type": "validation/string",
|
"type": "validation/string",
|
||||||
"label": "Validation",
|
"label": "Validation",
|
||||||
"key": "validation"
|
"key": "validation"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "select",
|
||||||
|
"label": "Layout",
|
||||||
|
"key": "span",
|
||||||
|
"defaultValue": 6,
|
||||||
|
"hidden": true,
|
||||||
|
"showInBar": true,
|
||||||
|
"barStyle": "buttons",
|
||||||
|
"options": [
|
||||||
|
{
|
||||||
|
"label": "1 column",
|
||||||
|
"value": 6,
|
||||||
|
"barIcon": "Stop",
|
||||||
|
"barTitle": "1 column"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "2 columns",
|
||||||
|
"value": 3,
|
||||||
|
"barIcon": "ColumnTwoA",
|
||||||
|
"barTitle": "2 columns"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "3 columns",
|
||||||
|
"value": 2,
|
||||||
|
"barIcon": "ViewColumn",
|
||||||
|
"barTitle": "3 columns"
|
||||||
|
}
|
||||||
|
]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
@ -3049,6 +3198,17 @@
|
||||||
"key": "disabled",
|
"key": "disabled",
|
||||||
"defaultValue": false
|
"defaultValue": false
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"type": "boolean",
|
||||||
|
"label": "Read only",
|
||||||
|
"key": "readonly",
|
||||||
|
"defaultValue": false,
|
||||||
|
"dependsOn": {
|
||||||
|
"setting": "disabled",
|
||||||
|
"value": true,
|
||||||
|
"invert": true
|
||||||
|
}
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"type": "select",
|
"type": "select",
|
||||||
"label": "Options source",
|
"label": "Options source",
|
||||||
|
@ -3110,6 +3270,35 @@
|
||||||
"type": "validation/string",
|
"type": "validation/string",
|
||||||
"label": "Validation",
|
"label": "Validation",
|
||||||
"key": "validation"
|
"key": "validation"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "select",
|
||||||
|
"label": "Layout",
|
||||||
|
"key": "span",
|
||||||
|
"defaultValue": 6,
|
||||||
|
"hidden": true,
|
||||||
|
"showInBar": true,
|
||||||
|
"barStyle": "buttons",
|
||||||
|
"options": [
|
||||||
|
{
|
||||||
|
"label": "1 column",
|
||||||
|
"value": 6,
|
||||||
|
"barIcon": "Stop",
|
||||||
|
"barTitle": "1 column"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "2 columns",
|
||||||
|
"value": 3,
|
||||||
|
"barIcon": "ColumnTwoA",
|
||||||
|
"barTitle": "2 columns"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "3 columns",
|
||||||
|
"value": 2,
|
||||||
|
"barIcon": "ViewColumn",
|
||||||
|
"barTitle": "3 columns"
|
||||||
|
}
|
||||||
|
]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
@ -3174,6 +3363,17 @@
|
||||||
"key": "disabled",
|
"key": "disabled",
|
||||||
"defaultValue": false
|
"defaultValue": false
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"type": "boolean",
|
||||||
|
"label": "Read only",
|
||||||
|
"key": "readonly",
|
||||||
|
"defaultValue": false,
|
||||||
|
"dependsOn": {
|
||||||
|
"setting": "disabled",
|
||||||
|
"value": true,
|
||||||
|
"invert": true
|
||||||
|
}
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"type": "select",
|
"type": "select",
|
||||||
"label": "Type",
|
"label": "Type",
|
||||||
|
@ -3272,6 +3472,35 @@
|
||||||
"type": "validation/array",
|
"type": "validation/array",
|
||||||
"label": "Validation",
|
"label": "Validation",
|
||||||
"key": "validation"
|
"key": "validation"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "select",
|
||||||
|
"label": "Layout",
|
||||||
|
"key": "span",
|
||||||
|
"defaultValue": 6,
|
||||||
|
"hidden": true,
|
||||||
|
"showInBar": true,
|
||||||
|
"barStyle": "buttons",
|
||||||
|
"options": [
|
||||||
|
{
|
||||||
|
"label": "1 column",
|
||||||
|
"value": 6,
|
||||||
|
"barIcon": "Stop",
|
||||||
|
"barTitle": "1 column"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "2 columns",
|
||||||
|
"value": 3,
|
||||||
|
"barIcon": "ColumnTwoA",
|
||||||
|
"barTitle": "2 columns"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "3 columns",
|
||||||
|
"value": 2,
|
||||||
|
"barIcon": "ViewColumn",
|
||||||
|
"barTitle": "3 columns"
|
||||||
|
}
|
||||||
|
]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
@ -3348,10 +3577,50 @@
|
||||||
"key": "disabled",
|
"key": "disabled",
|
||||||
"defaultValue": false
|
"defaultValue": false
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"type": "boolean",
|
||||||
|
"label": "Read only",
|
||||||
|
"key": "readonly",
|
||||||
|
"defaultValue": false,
|
||||||
|
"dependsOn": {
|
||||||
|
"setting": "disabled",
|
||||||
|
"value": true,
|
||||||
|
"invert": true
|
||||||
|
}
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"type": "validation/boolean",
|
"type": "validation/boolean",
|
||||||
"label": "Validation",
|
"label": "Validation",
|
||||||
"key": "validation"
|
"key": "validation"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "select",
|
||||||
|
"label": "Layout",
|
||||||
|
"key": "span",
|
||||||
|
"defaultValue": 6,
|
||||||
|
"hidden": true,
|
||||||
|
"showInBar": true,
|
||||||
|
"barStyle": "buttons",
|
||||||
|
"options": [
|
||||||
|
{
|
||||||
|
"label": "1 column",
|
||||||
|
"value": 6,
|
||||||
|
"barIcon": "Stop",
|
||||||
|
"barTitle": "1 column"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "2 columns",
|
||||||
|
"value": 3,
|
||||||
|
"barIcon": "ColumnTwoA",
|
||||||
|
"barTitle": "2 columns"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "3 columns",
|
||||||
|
"value": 2,
|
||||||
|
"barIcon": "ViewColumn",
|
||||||
|
"barTitle": "3 columns"
|
||||||
|
}
|
||||||
|
]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
@ -3427,10 +3696,50 @@
|
||||||
"key": "disabled",
|
"key": "disabled",
|
||||||
"defaultValue": false
|
"defaultValue": false
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"type": "boolean",
|
||||||
|
"label": "Read only",
|
||||||
|
"key": "readonly",
|
||||||
|
"defaultValue": false,
|
||||||
|
"dependsOn": {
|
||||||
|
"setting": "disabled",
|
||||||
|
"value": true,
|
||||||
|
"invert": true
|
||||||
|
}
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"type": "validation/string",
|
"type": "validation/string",
|
||||||
"label": "Validation",
|
"label": "Validation",
|
||||||
"key": "validation"
|
"key": "validation"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "select",
|
||||||
|
"label": "Layout",
|
||||||
|
"key": "span",
|
||||||
|
"defaultValue": 6,
|
||||||
|
"hidden": true,
|
||||||
|
"showInBar": true,
|
||||||
|
"barStyle": "buttons",
|
||||||
|
"options": [
|
||||||
|
{
|
||||||
|
"label": "1 column",
|
||||||
|
"value": 6,
|
||||||
|
"barIcon": "Stop",
|
||||||
|
"barTitle": "1 column"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "2 columns",
|
||||||
|
"value": 3,
|
||||||
|
"barIcon": "ColumnTwoA",
|
||||||
|
"barTitle": "2 columns"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "3 columns",
|
||||||
|
"value": 2,
|
||||||
|
"barIcon": "ViewColumn",
|
||||||
|
"barTitle": "3 columns"
|
||||||
|
}
|
||||||
|
]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
@ -3508,10 +3817,50 @@
|
||||||
"key": "disabled",
|
"key": "disabled",
|
||||||
"defaultValue": false
|
"defaultValue": false
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"type": "boolean",
|
||||||
|
"label": "Read only",
|
||||||
|
"key": "readonly",
|
||||||
|
"defaultValue": false,
|
||||||
|
"dependsOn": {
|
||||||
|
"setting": "disabled",
|
||||||
|
"value": true,
|
||||||
|
"invert": true
|
||||||
|
}
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"type": "validation/datetime",
|
"type": "validation/datetime",
|
||||||
"label": "Validation",
|
"label": "Validation",
|
||||||
"key": "validation"
|
"key": "validation"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "select",
|
||||||
|
"label": "Layout",
|
||||||
|
"key": "span",
|
||||||
|
"defaultValue": 6,
|
||||||
|
"hidden": true,
|
||||||
|
"showInBar": true,
|
||||||
|
"barStyle": "buttons",
|
||||||
|
"options": [
|
||||||
|
{
|
||||||
|
"label": "1 column",
|
||||||
|
"value": 6,
|
||||||
|
"barIcon": "Stop",
|
||||||
|
"barTitle": "1 column"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "2 columns",
|
||||||
|
"value": 3,
|
||||||
|
"barIcon": "ColumnTwoA",
|
||||||
|
"barTitle": "2 columns"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "3 columns",
|
||||||
|
"value": 2,
|
||||||
|
"barIcon": "ViewColumn",
|
||||||
|
"barTitle": "3 columns"
|
||||||
|
}
|
||||||
|
]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
@ -3598,6 +3947,22 @@
|
||||||
"value": "custom"
|
"value": "custom"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"type": "select",
|
||||||
|
"label": "Preferred camera",
|
||||||
|
"key": "preferredCamera",
|
||||||
|
"defaultValue": "environment",
|
||||||
|
"options": [
|
||||||
|
{
|
||||||
|
"label": "Front",
|
||||||
|
"value": "user"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "Back",
|
||||||
|
"value": "environment"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"type": "event",
|
"type": "event",
|
||||||
"label": "On change",
|
"label": "On change",
|
||||||
|
@ -3613,6 +3978,35 @@
|
||||||
"type": "validation/string",
|
"type": "validation/string",
|
||||||
"label": "Validation",
|
"label": "Validation",
|
||||||
"key": "validation"
|
"key": "validation"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "select",
|
||||||
|
"label": "Layout",
|
||||||
|
"key": "span",
|
||||||
|
"defaultValue": 6,
|
||||||
|
"hidden": true,
|
||||||
|
"showInBar": true,
|
||||||
|
"barStyle": "buttons",
|
||||||
|
"options": [
|
||||||
|
{
|
||||||
|
"label": "1 column",
|
||||||
|
"value": 6,
|
||||||
|
"barIcon": "Stop",
|
||||||
|
"barTitle": "1 column"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "2 columns",
|
||||||
|
"value": 3,
|
||||||
|
"barIcon": "ColumnTwoA",
|
||||||
|
"barTitle": "2 columns"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "3 columns",
|
||||||
|
"value": 2,
|
||||||
|
"barIcon": "ViewColumn",
|
||||||
|
"barTitle": "3 columns"
|
||||||
|
}
|
||||||
|
]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
@ -3781,7 +4175,7 @@
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"type": "boolean",
|
"type": "boolean",
|
||||||
"label": "Disabled",
|
"label": "Read only",
|
||||||
"key": "disabled",
|
"key": "disabled",
|
||||||
"defaultValue": false
|
"defaultValue": false
|
||||||
},
|
},
|
||||||
|
@ -3789,6 +4183,35 @@
|
||||||
"type": "validation/attachment",
|
"type": "validation/attachment",
|
||||||
"label": "Validation",
|
"label": "Validation",
|
||||||
"key": "validation"
|
"key": "validation"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "select",
|
||||||
|
"label": "Layout",
|
||||||
|
"key": "span",
|
||||||
|
"defaultValue": 6,
|
||||||
|
"hidden": true,
|
||||||
|
"showInBar": true,
|
||||||
|
"barStyle": "buttons",
|
||||||
|
"options": [
|
||||||
|
{
|
||||||
|
"label": "1 column",
|
||||||
|
"value": 6,
|
||||||
|
"barIcon": "Stop",
|
||||||
|
"barTitle": "1 column"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "2 columns",
|
||||||
|
"value": 3,
|
||||||
|
"barIcon": "ColumnTwoA",
|
||||||
|
"barTitle": "2 columns"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "3 columns",
|
||||||
|
"value": 2,
|
||||||
|
"barIcon": "ViewColumn",
|
||||||
|
"barTitle": "3 columns"
|
||||||
|
}
|
||||||
|
]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
@ -3857,6 +4280,46 @@
|
||||||
"label": "Disabled",
|
"label": "Disabled",
|
||||||
"key": "disabled",
|
"key": "disabled",
|
||||||
"defaultValue": false
|
"defaultValue": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "boolean",
|
||||||
|
"label": "Read only",
|
||||||
|
"key": "readonly",
|
||||||
|
"defaultValue": false,
|
||||||
|
"dependsOn": {
|
||||||
|
"setting": "disabled",
|
||||||
|
"value": true,
|
||||||
|
"invert": true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "select",
|
||||||
|
"label": "Layout",
|
||||||
|
"key": "span",
|
||||||
|
"defaultValue": 6,
|
||||||
|
"hidden": true,
|
||||||
|
"showInBar": true,
|
||||||
|
"barStyle": "buttons",
|
||||||
|
"options": [
|
||||||
|
{
|
||||||
|
"label": "1 column",
|
||||||
|
"value": 6,
|
||||||
|
"barIcon": "Stop",
|
||||||
|
"barTitle": "1 column"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "2 columns",
|
||||||
|
"value": 3,
|
||||||
|
"barIcon": "ColumnTwoA",
|
||||||
|
"barTitle": "2 columns"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "3 columns",
|
||||||
|
"value": 2,
|
||||||
|
"barIcon": "ViewColumn",
|
||||||
|
"barTitle": "3 columns"
|
||||||
|
}
|
||||||
|
]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
@ -3909,6 +4372,46 @@
|
||||||
"label": "Disabled",
|
"label": "Disabled",
|
||||||
"key": "disabled",
|
"key": "disabled",
|
||||||
"defaultValue": false
|
"defaultValue": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "boolean",
|
||||||
|
"label": "Read only",
|
||||||
|
"key": "readonly",
|
||||||
|
"defaultValue": false,
|
||||||
|
"dependsOn": {
|
||||||
|
"setting": "disabled",
|
||||||
|
"value": true,
|
||||||
|
"invert": true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "select",
|
||||||
|
"label": "Layout",
|
||||||
|
"key": "span",
|
||||||
|
"defaultValue": 6,
|
||||||
|
"hidden": true,
|
||||||
|
"showInBar": true,
|
||||||
|
"barStyle": "buttons",
|
||||||
|
"options": [
|
||||||
|
{
|
||||||
|
"label": "1 column",
|
||||||
|
"value": 6,
|
||||||
|
"barIcon": "Stop",
|
||||||
|
"barTitle": "1 column"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "2 columns",
|
||||||
|
"value": 3,
|
||||||
|
"barIcon": "ColumnTwoA",
|
||||||
|
"barTitle": "2 columns"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "3 columns",
|
||||||
|
"value": 2,
|
||||||
|
"barIcon": "ViewColumn",
|
||||||
|
"barTitle": "3 columns"
|
||||||
|
}
|
||||||
|
]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
@ -5530,12 +6033,7 @@
|
||||||
"type": "boolean",
|
"type": "boolean",
|
||||||
"label": "Disabled",
|
"label": "Disabled",
|
||||||
"key": "disabled",
|
"key": "disabled",
|
||||||
"defaultValue": false,
|
"defaultValue": false
|
||||||
"dependsOn": {
|
|
||||||
"setting": "actionType",
|
|
||||||
"value": "View",
|
|
||||||
"invert": true
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
@ -5590,23 +6088,6 @@
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"tag": "style",
|
|
||||||
"type": "select",
|
|
||||||
"label": "Align labels",
|
|
||||||
"key": "labelPosition",
|
|
||||||
"defaultValue": "left",
|
|
||||||
"options": [
|
|
||||||
{
|
|
||||||
"label": "Left",
|
|
||||||
"value": "left"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"label": "Above",
|
|
||||||
"value": "above"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"tag": "style",
|
"tag": "style",
|
||||||
"type": "select",
|
"type": "select",
|
||||||
|
@ -5921,6 +6402,35 @@
|
||||||
"label": "Disabled",
|
"label": "Disabled",
|
||||||
"key": "disabled",
|
"key": "disabled",
|
||||||
"defaultValue": false
|
"defaultValue": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "select",
|
||||||
|
"label": "Layout",
|
||||||
|
"key": "span",
|
||||||
|
"defaultValue": 6,
|
||||||
|
"hidden": true,
|
||||||
|
"showInBar": true,
|
||||||
|
"barStyle": "buttons",
|
||||||
|
"options": [
|
||||||
|
{
|
||||||
|
"label": "1 column",
|
||||||
|
"value": 6,
|
||||||
|
"barIcon": "Stop",
|
||||||
|
"barTitle": "1 column"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "2 columns",
|
||||||
|
"value": 3,
|
||||||
|
"barIcon": "ColumnTwoA",
|
||||||
|
"barTitle": "2 columns"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "3 columns",
|
||||||
|
"value": 2,
|
||||||
|
"barIcon": "ViewColumn",
|
||||||
|
"barTitle": "3 columns"
|
||||||
|
}
|
||||||
|
]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
|
@ -26,15 +26,15 @@
|
||||||
$: parentId = $component?.id
|
$: parentId = $component?.id
|
||||||
$: inBuilder = $builderStore.inBuilder
|
$: inBuilder = $builderStore.inBuilder
|
||||||
$: instance = {
|
$: instance = {
|
||||||
|
...props,
|
||||||
_component: getComponent(type),
|
_component: getComponent(type),
|
||||||
_id: id,
|
_id: id,
|
||||||
_instanceName: getInstanceName(name, type),
|
_instanceName: getInstanceName(name, type),
|
||||||
|
_containsSlot: containsSlot,
|
||||||
_styles: {
|
_styles: {
|
||||||
...styles,
|
...styles,
|
||||||
normal: styles?.normal || {},
|
normal: styles?.normal || {},
|
||||||
},
|
},
|
||||||
_containsSlot: containsSlot,
|
|
||||||
...props,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Register this block component if we're inside the builder so it can be
|
// Register this block component if we're inside the builder so it can be
|
||||||
|
|
|
@ -140,6 +140,7 @@
|
||||||
interactive &&
|
interactive &&
|
||||||
!isLayout &&
|
!isLayout &&
|
||||||
!isRoot &&
|
!isRoot &&
|
||||||
|
!isBlock &&
|
||||||
definition?.draggable !== false
|
definition?.draggable !== false
|
||||||
$: droppable = interactive
|
$: droppable = interactive
|
||||||
$: builderHidden =
|
$: builderHidden =
|
||||||
|
@ -194,6 +195,7 @@
|
||||||
interactive,
|
interactive,
|
||||||
draggable,
|
draggable,
|
||||||
editable,
|
editable,
|
||||||
|
isBlock,
|
||||||
},
|
},
|
||||||
empty: emptyState,
|
empty: emptyState,
|
||||||
selected,
|
selected,
|
||||||
|
|
|
@ -10,7 +10,6 @@
|
||||||
export let size
|
export let size
|
||||||
export let disabled
|
export let disabled
|
||||||
export let fields
|
export let fields
|
||||||
export let labelPosition
|
|
||||||
export let title
|
export let title
|
||||||
export let description
|
export let description
|
||||||
export let showDeleteButton
|
export let showDeleteButton
|
||||||
|
@ -97,7 +96,6 @@
|
||||||
size,
|
size,
|
||||||
disabled,
|
disabled,
|
||||||
fields: fieldsOrDefault,
|
fields: fieldsOrDefault,
|
||||||
labelPosition,
|
|
||||||
title,
|
title,
|
||||||
description,
|
description,
|
||||||
saveButtonLabel: saveLabel,
|
saveButtonLabel: saveLabel,
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
import BlockComponent from "components/BlockComponent.svelte"
|
import BlockComponent from "components/BlockComponent.svelte"
|
||||||
import Placeholder from "components/app/Placeholder.svelte"
|
import Placeholder from "components/app/Placeholder.svelte"
|
||||||
import { makePropSafe as safe } from "@budibase/string-templates"
|
import { makePropSafe as safe } from "@budibase/string-templates"
|
||||||
|
import { getContext } from "svelte"
|
||||||
|
|
||||||
export let dataSource
|
export let dataSource
|
||||||
export let actionUrl
|
export let actionUrl
|
||||||
|
@ -9,7 +10,6 @@
|
||||||
export let size
|
export let size
|
||||||
export let disabled
|
export let disabled
|
||||||
export let fields
|
export let fields
|
||||||
export let labelPosition
|
|
||||||
export let title
|
export let title
|
||||||
export let description
|
export let description
|
||||||
export let saveButtonLabel
|
export let saveButtonLabel
|
||||||
|
@ -33,6 +33,7 @@
|
||||||
barcodeqr: "codescanner",
|
barcodeqr: "codescanner",
|
||||||
bb_reference: "bbreferencefield",
|
bb_reference: "bbreferencefield",
|
||||||
}
|
}
|
||||||
|
const context = getContext("context")
|
||||||
|
|
||||||
let formId
|
let formId
|
||||||
|
|
||||||
|
@ -136,7 +137,8 @@
|
||||||
actionType: actionType === "Create" ? "Create" : "Update",
|
actionType: actionType === "Create" ? "Create" : "Update",
|
||||||
dataSource,
|
dataSource,
|
||||||
size,
|
size,
|
||||||
disabled: disabled || actionType === "View",
|
disabled,
|
||||||
|
readonly: !disabled && actionType === "View",
|
||||||
}}
|
}}
|
||||||
styles={{
|
styles={{
|
||||||
normal: {
|
normal: {
|
||||||
|
@ -226,16 +228,20 @@
|
||||||
<BlockComponent type="text" props={{ text: description }} order={1} />
|
<BlockComponent type="text" props={{ text: description }} order={1} />
|
||||||
{/if}
|
{/if}
|
||||||
{#key fields}
|
{#key fields}
|
||||||
<BlockComponent type="fieldgroup" props={{ labelPosition }} order={1}>
|
<BlockComponent type="container">
|
||||||
{#each fields as field, idx}
|
<div class="form-block fields" class:mobile={$context.device.mobile}>
|
||||||
{#if getComponentForField(field) && field.active}
|
{#each fields as field, idx}
|
||||||
<BlockComponent
|
{#if getComponentForField(field) && field.active}
|
||||||
type={getComponentForField(field)}
|
<BlockComponent
|
||||||
props={getPropsForField(field)}
|
type={getComponentForField(field)}
|
||||||
order={idx}
|
props={getPropsForField(field)}
|
||||||
/>
|
order={idx}
|
||||||
{/if}
|
interactive
|
||||||
{/each}
|
name={field?.field}
|
||||||
|
/>
|
||||||
|
{/if}
|
||||||
|
{/each}
|
||||||
|
</div>
|
||||||
</BlockComponent>
|
</BlockComponent>
|
||||||
{/key}
|
{/key}
|
||||||
</BlockComponent>
|
</BlockComponent>
|
||||||
|
@ -245,3 +251,14 @@
|
||||||
text="Choose your table and add some fields to your form to get started"
|
text="Choose your table and add some fields to your form to get started"
|
||||||
/>
|
/>
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.fields {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(6, 1fr);
|
||||||
|
gap: 8px 16px;
|
||||||
|
}
|
||||||
|
.fields.mobile :global(.spectrum-Form-item) {
|
||||||
|
grid-column: span 6 !important;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
|
@ -6,11 +6,13 @@
|
||||||
export let field
|
export let field
|
||||||
export let label
|
export let label
|
||||||
export let disabled = false
|
export let disabled = false
|
||||||
|
export let readonly = false
|
||||||
export let compact = false
|
export let compact = false
|
||||||
export let validation
|
export let validation
|
||||||
export let extensions
|
export let extensions
|
||||||
export let onChange
|
export let onChange
|
||||||
export let maximum = undefined
|
export let maximum = undefined
|
||||||
|
export let span
|
||||||
|
|
||||||
let fieldState
|
let fieldState
|
||||||
let fieldApi
|
let fieldApi
|
||||||
|
@ -71,33 +73,27 @@
|
||||||
{label}
|
{label}
|
||||||
{field}
|
{field}
|
||||||
{disabled}
|
{disabled}
|
||||||
|
{readonly}
|
||||||
{validation}
|
{validation}
|
||||||
|
{span}
|
||||||
type="attachment"
|
type="attachment"
|
||||||
bind:fieldState
|
bind:fieldState
|
||||||
bind:fieldApi
|
bind:fieldApi
|
||||||
defaultValue={[]}
|
defaultValue={[]}
|
||||||
>
|
>
|
||||||
<div class="minHeightWrapper">
|
{#if fieldState}
|
||||||
{#if fieldState}
|
<CoreDropzone
|
||||||
<CoreDropzone
|
value={fieldState.value}
|
||||||
value={fieldState.value}
|
disabled={fieldState.disabled || fieldState.readonly}
|
||||||
disabled={fieldState.disabled}
|
error={fieldState.error}
|
||||||
error={fieldState.error}
|
on:change={handleChange}
|
||||||
on:change={handleChange}
|
{processFiles}
|
||||||
{processFiles}
|
{deleteAttachments}
|
||||||
{deleteAttachments}
|
{handleFileTooLarge}
|
||||||
{handleFileTooLarge}
|
{handleTooManyFiles}
|
||||||
{handleTooManyFiles}
|
{maximum}
|
||||||
{maximum}
|
{extensions}
|
||||||
{extensions}
|
{compact}
|
||||||
{compact}
|
/>
|
||||||
/>
|
{/if}
|
||||||
{/if}
|
|
||||||
</div>
|
|
||||||
</Field>
|
</Field>
|
||||||
|
|
||||||
<style>
|
|
||||||
.minHeightWrapper {
|
|
||||||
min-height: 80px;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
|
|
@ -6,6 +6,7 @@
|
||||||
export let label
|
export let label
|
||||||
export let text
|
export let text
|
||||||
export let disabled = false
|
export let disabled = false
|
||||||
|
export let readonly = false
|
||||||
export let size
|
export let size
|
||||||
export let validation
|
export let validation
|
||||||
export let defaultValue
|
export let defaultValue
|
||||||
|
@ -39,6 +40,7 @@
|
||||||
{label}
|
{label}
|
||||||
{field}
|
{field}
|
||||||
{disabled}
|
{disabled}
|
||||||
|
{readonly}
|
||||||
{validation}
|
{validation}
|
||||||
defaultValue={isTruthy(defaultValue)}
|
defaultValue={isTruthy(defaultValue)}
|
||||||
type="boolean"
|
type="boolean"
|
||||||
|
@ -49,6 +51,7 @@
|
||||||
<CoreCheckbox
|
<CoreCheckbox
|
||||||
value={fieldState.value}
|
value={fieldState.value}
|
||||||
disabled={fieldState.disabled}
|
disabled={fieldState.disabled}
|
||||||
|
readonly={fieldState.readonly}
|
||||||
error={fieldState.error}
|
error={fieldState.error}
|
||||||
id={fieldState.fieldId}
|
id={fieldState.fieldId}
|
||||||
{size}
|
{size}
|
||||||
|
|
|
@ -11,6 +11,7 @@
|
||||||
export let beepOnScan = false
|
export let beepOnScan = false
|
||||||
export let beepFrequency = 2637
|
export let beepFrequency = 2637
|
||||||
export let customFrequency = 1046
|
export let customFrequency = 1046
|
||||||
|
export let preferredCamera = "environment"
|
||||||
|
|
||||||
const dispatch = createEventDispatcher()
|
const dispatch = createEventDispatcher()
|
||||||
|
|
||||||
|
@ -20,7 +21,7 @@
|
||||||
let cameraEnabled
|
let cameraEnabled
|
||||||
let cameraStarted = false
|
let cameraStarted = false
|
||||||
let html5QrCode
|
let html5QrCode
|
||||||
let cameraSetting = { facingMode: "environment" }
|
let cameraSetting = { facingMode: preferredCamera }
|
||||||
let cameraConfig = {
|
let cameraConfig = {
|
||||||
fps: 25,
|
fps: 25,
|
||||||
qrbox: { width: 250, height: 250 },
|
qrbox: { width: 250, height: 250 },
|
||||||
|
|
|
@ -6,6 +6,7 @@
|
||||||
export let label
|
export let label
|
||||||
export let type = "barcodeqr"
|
export let type = "barcodeqr"
|
||||||
export let disabled = false
|
export let disabled = false
|
||||||
|
export let readonly = false
|
||||||
export let validation
|
export let validation
|
||||||
export let defaultValue = ""
|
export let defaultValue = ""
|
||||||
export let onChange
|
export let onChange
|
||||||
|
@ -14,6 +15,7 @@
|
||||||
export let beepOnScan
|
export let beepOnScan
|
||||||
export let beepFrequency
|
export let beepFrequency
|
||||||
export let customFrequency
|
export let customFrequency
|
||||||
|
export let preferredCamera
|
||||||
|
|
||||||
let fieldState
|
let fieldState
|
||||||
let fieldApi
|
let fieldApi
|
||||||
|
@ -32,6 +34,7 @@
|
||||||
{label}
|
{label}
|
||||||
{field}
|
{field}
|
||||||
{disabled}
|
{disabled}
|
||||||
|
{readonly}
|
||||||
{validation}
|
{validation}
|
||||||
{defaultValue}
|
{defaultValue}
|
||||||
{type}
|
{type}
|
||||||
|
@ -42,12 +45,13 @@
|
||||||
<CodeScanner
|
<CodeScanner
|
||||||
value={fieldState.value}
|
value={fieldState.value}
|
||||||
on:change={handleUpdate}
|
on:change={handleUpdate}
|
||||||
disabled={fieldState.disabled}
|
disabled={fieldState.disabled || fieldState.readonly}
|
||||||
{allowManualEntry}
|
{allowManualEntry}
|
||||||
scanButtonText={scanText}
|
scanButtonText={scanText}
|
||||||
{beepOnScan}
|
{beepOnScan}
|
||||||
{beepFrequency}
|
{beepFrequency}
|
||||||
{customFrequency}
|
{customFrequency}
|
||||||
|
{preferredCamera}
|
||||||
/>
|
/>
|
||||||
{/if}
|
{/if}
|
||||||
</Field>
|
</Field>
|
||||||
|
|
|
@ -6,6 +6,7 @@
|
||||||
export let label
|
export let label
|
||||||
export let placeholder
|
export let placeholder
|
||||||
export let disabled = false
|
export let disabled = false
|
||||||
|
export let readonly = false
|
||||||
export let enableTime = true
|
export let enableTime = true
|
||||||
export let timeOnly = false
|
export let timeOnly = false
|
||||||
export let time24hr = false
|
export let time24hr = false
|
||||||
|
@ -13,6 +14,7 @@
|
||||||
export let validation
|
export let validation
|
||||||
export let defaultValue
|
export let defaultValue
|
||||||
export let onChange
|
export let onChange
|
||||||
|
export let span
|
||||||
|
|
||||||
let fieldState
|
let fieldState
|
||||||
let fieldApi
|
let fieldApi
|
||||||
|
@ -29,8 +31,10 @@
|
||||||
{label}
|
{label}
|
||||||
{field}
|
{field}
|
||||||
{disabled}
|
{disabled}
|
||||||
|
{readonly}
|
||||||
{validation}
|
{validation}
|
||||||
{defaultValue}
|
{defaultValue}
|
||||||
|
{span}
|
||||||
type="datetime"
|
type="datetime"
|
||||||
bind:fieldState
|
bind:fieldState
|
||||||
bind:fieldApi
|
bind:fieldApi
|
||||||
|
@ -40,6 +44,7 @@
|
||||||
value={fieldState.value}
|
value={fieldState.value}
|
||||||
on:change={handleChange}
|
on:change={handleChange}
|
||||||
disabled={fieldState.disabled}
|
disabled={fieldState.disabled}
|
||||||
|
readonly={fieldState.readonly}
|
||||||
error={fieldState.error}
|
error={fieldState.error}
|
||||||
id={fieldState.fieldId}
|
id={fieldState.fieldId}
|
||||||
appendTo={document.getElementById("flatpickr-root")}
|
appendTo={document.getElementById("flatpickr-root")}
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
<script>
|
<script>
|
||||||
import Placeholder from "../Placeholder.svelte"
|
import Placeholder from "../Placeholder.svelte"
|
||||||
import FieldGroupFallback from "./FieldGroupFallback.svelte"
|
|
||||||
import { getContext, onDestroy } from "svelte"
|
import { getContext, onDestroy } from "svelte"
|
||||||
|
|
||||||
export let label
|
export let label
|
||||||
|
@ -11,7 +10,9 @@
|
||||||
export let defaultValue
|
export let defaultValue
|
||||||
export let type
|
export let type
|
||||||
export let disabled = false
|
export let disabled = false
|
||||||
|
export let readonly = false
|
||||||
export let validation
|
export let validation
|
||||||
|
export let span = 6
|
||||||
|
|
||||||
// Get contexts
|
// Get contexts
|
||||||
const formContext = getContext("form")
|
const formContext = getContext("form")
|
||||||
|
@ -29,6 +30,7 @@
|
||||||
type,
|
type,
|
||||||
defaultValue,
|
defaultValue,
|
||||||
disabled,
|
disabled,
|
||||||
|
readonly,
|
||||||
validation,
|
validation,
|
||||||
formStep
|
formStep
|
||||||
)
|
)
|
||||||
|
@ -62,40 +64,59 @@
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<FieldGroupFallback>
|
<div
|
||||||
<div class="spectrum-Form-item" use:styleable={$component.styles}>
|
class="spectrum-Form-item"
|
||||||
{#key $component.editing}
|
class:span-2={span === 2}
|
||||||
<label
|
class:span-3={span === 3}
|
||||||
bind:this={labelNode}
|
class:span-6={span === 6 || !span}
|
||||||
contenteditable={$component.editing}
|
use:styleable={$component.styles}
|
||||||
on:blur={$component.editing ? updateLabel : null}
|
class:above={labelPos === "above"}
|
||||||
class:hidden={!label}
|
>
|
||||||
for={fieldState?.fieldId}
|
{#key $component.editing}
|
||||||
class={`spectrum-FieldLabel spectrum-FieldLabel--sizeM spectrum-Form-itemLabel ${labelClass}`}
|
<label
|
||||||
>
|
bind:this={labelNode}
|
||||||
{label || " "}
|
contenteditable={$component.editing}
|
||||||
</label>
|
on:blur={$component.editing ? updateLabel : null}
|
||||||
{/key}
|
class:hidden={!label}
|
||||||
<div class="spectrum-Form-itemField">
|
class:readonly
|
||||||
{#if !formContext}
|
for={fieldState?.fieldId}
|
||||||
<Placeholder text="Form components need to be wrapped in a form" />
|
class={`spectrum-FieldLabel spectrum-FieldLabel--sizeM spectrum-Form-itemLabel ${labelClass}`}
|
||||||
{:else if !fieldState}
|
>
|
||||||
<Placeholder />
|
{label || " "}
|
||||||
{:else if schemaType && schemaType !== type && !["options", "longform"].includes(type)}
|
</label>
|
||||||
<Placeholder
|
{/key}
|
||||||
text="This Field setting is the wrong data type for this component"
|
<div class="spectrum-Form-itemField">
|
||||||
/>
|
{#if !formContext}
|
||||||
{:else}
|
<Placeholder text="Form components need to be wrapped in a form" />
|
||||||
<slot />
|
{:else if !fieldState}
|
||||||
{#if fieldState.error}
|
<Placeholder />
|
||||||
<div class="error">{fieldState.error}</div>
|
{:else if schemaType && schemaType !== type && !["options", "longform"].includes(type)}
|
||||||
{/if}
|
<Placeholder
|
||||||
|
text="This Field setting is the wrong data type for this component"
|
||||||
|
/>
|
||||||
|
{:else}
|
||||||
|
<slot />
|
||||||
|
{#if fieldState.error}
|
||||||
|
<div class="error">{fieldState.error}</div>
|
||||||
{/if}
|
{/if}
|
||||||
</div>
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
</FieldGroupFallback>
|
</div>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
|
:global(.form-block .spectrum-Form-item.span-2) {
|
||||||
|
grid-column: span 2;
|
||||||
|
}
|
||||||
|
:global(.form-block .spectrum-Form-item.span-3) {
|
||||||
|
grid-column: span 3;
|
||||||
|
}
|
||||||
|
:global(.form-block .spectrum-Form-item.span-6) {
|
||||||
|
grid-column: span 6;
|
||||||
|
}
|
||||||
|
.spectrum-Form-item.above {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
label {
|
label {
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
}
|
}
|
||||||
|
@ -118,4 +139,7 @@
|
||||||
.spectrum-FieldLabel--left {
|
.spectrum-FieldLabel--left {
|
||||||
padding-right: var(--spectrum-global-dimension-size-200);
|
padding-right: var(--spectrum-global-dimension-size-200);
|
||||||
}
|
}
|
||||||
|
.readonly {
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
@ -8,6 +8,7 @@
|
||||||
export let theme
|
export let theme
|
||||||
export let size
|
export let size
|
||||||
export let disabled = false
|
export let disabled = false
|
||||||
|
export let readonly = false
|
||||||
export let actionType = "Create"
|
export let actionType = "Create"
|
||||||
export let initialFormStep = 1
|
export let initialFormStep = 1
|
||||||
|
|
||||||
|
@ -39,7 +40,7 @@
|
||||||
$: schemaKey = generateSchemaKey(schema)
|
$: schemaKey = generateSchemaKey(schema)
|
||||||
$: initialValues = getInitialValues(actionType, dataSource, $context)
|
$: initialValues = getInitialValues(actionType, dataSource, $context)
|
||||||
$: resetKey = Helpers.hashString(
|
$: resetKey = Helpers.hashString(
|
||||||
schemaKey + JSON.stringify(initialValues) + disabled
|
schemaKey + JSON.stringify(initialValues) + disabled + readonly
|
||||||
)
|
)
|
||||||
|
|
||||||
// Returns the closes data context which isn't a built in context
|
// Returns the closes data context which isn't a built in context
|
||||||
|
@ -97,6 +98,7 @@
|
||||||
{theme}
|
{theme}
|
||||||
{size}
|
{size}
|
||||||
{disabled}
|
{disabled}
|
||||||
|
{readonly}
|
||||||
{actionType}
|
{actionType}
|
||||||
{schema}
|
{schema}
|
||||||
{table}
|
{table}
|
||||||
|
|
|
@ -6,6 +6,7 @@
|
||||||
|
|
||||||
export let dataSource
|
export let dataSource
|
||||||
export let disabled = false
|
export let disabled = false
|
||||||
|
export let readonly = false
|
||||||
export let initialValues
|
export let initialValues
|
||||||
export let size
|
export let size
|
||||||
export let schema
|
export let schema
|
||||||
|
@ -148,6 +149,7 @@
|
||||||
type,
|
type,
|
||||||
defaultValue = null,
|
defaultValue = null,
|
||||||
fieldDisabled = false,
|
fieldDisabled = false,
|
||||||
|
fieldReadOnly = false,
|
||||||
validationRules,
|
validationRules,
|
||||||
step = 1
|
step = 1
|
||||||
) => {
|
) => {
|
||||||
|
@ -205,6 +207,7 @@
|
||||||
error: initialError,
|
error: initialError,
|
||||||
disabled:
|
disabled:
|
||||||
disabled || fieldDisabled || (isAutoColumn && !editAutoColumns),
|
disabled || fieldDisabled || (isAutoColumn && !editAutoColumns),
|
||||||
|
readonly: readonly || fieldReadOnly,
|
||||||
defaultValue,
|
defaultValue,
|
||||||
validator,
|
validator,
|
||||||
lastUpdate: Date.now(),
|
lastUpdate: Date.now(),
|
||||||
|
|
|
@ -7,6 +7,7 @@
|
||||||
export let label
|
export let label
|
||||||
export let placeholder
|
export let placeholder
|
||||||
export let disabled = false
|
export let disabled = false
|
||||||
|
export let readonly = false
|
||||||
export let defaultValue = ""
|
export let defaultValue = ""
|
||||||
export let onChange
|
export let onChange
|
||||||
|
|
||||||
|
@ -48,6 +49,7 @@
|
||||||
{label}
|
{label}
|
||||||
{field}
|
{field}
|
||||||
{disabled}
|
{disabled}
|
||||||
|
{readonly}
|
||||||
{validation}
|
{validation}
|
||||||
{defaultValue}
|
{defaultValue}
|
||||||
type="json"
|
type="json"
|
||||||
|
@ -60,6 +62,7 @@
|
||||||
value={serialiseValue(fieldState.value)}
|
value={serialiseValue(fieldState.value)}
|
||||||
on:change={handleChange}
|
on:change={handleChange}
|
||||||
disabled={fieldState.disabled}
|
disabled={fieldState.disabled}
|
||||||
|
readonly={fieldState.readonly}
|
||||||
error={fieldState.error}
|
error={fieldState.error}
|
||||||
id={fieldState.fieldId}
|
id={fieldState.fieldId}
|
||||||
{placeholder}
|
{placeholder}
|
||||||
|
|
|
@ -8,6 +8,7 @@
|
||||||
export let label
|
export let label
|
||||||
export let placeholder
|
export let placeholder
|
||||||
export let disabled = false
|
export let disabled = false
|
||||||
|
export let readonly = false
|
||||||
export let validation
|
export let validation
|
||||||
export let defaultValue = ""
|
export let defaultValue = ""
|
||||||
export let format = "auto"
|
export let format = "auto"
|
||||||
|
@ -58,6 +59,7 @@
|
||||||
{label}
|
{label}
|
||||||
{field}
|
{field}
|
||||||
{disabled}
|
{disabled}
|
||||||
|
{readonly}
|
||||||
{validation}
|
{validation}
|
||||||
{defaultValue}
|
{defaultValue}
|
||||||
type="longform"
|
type="longform"
|
||||||
|
@ -71,6 +73,7 @@
|
||||||
value={fieldState.value}
|
value={fieldState.value}
|
||||||
on:change={handleChange}
|
on:change={handleChange}
|
||||||
disabled={fieldState.disabled}
|
disabled={fieldState.disabled}
|
||||||
|
readonly={fieldState.readonly}
|
||||||
error={fieldState.error}
|
error={fieldState.error}
|
||||||
id={fieldState.fieldId}
|
id={fieldState.fieldId}
|
||||||
{placeholder}
|
{placeholder}
|
||||||
|
@ -88,6 +91,7 @@
|
||||||
value={fieldState.value}
|
value={fieldState.value}
|
||||||
on:change={handleChange}
|
on:change={handleChange}
|
||||||
disabled={fieldState.disabled}
|
disabled={fieldState.disabled}
|
||||||
|
readonly={fieldState.readonly}
|
||||||
error={fieldState.error}
|
error={fieldState.error}
|
||||||
id={fieldState.fieldId}
|
id={fieldState.fieldId}
|
||||||
{placeholder}
|
{placeholder}
|
||||||
|
|
|
@ -6,6 +6,7 @@
|
||||||
export let label
|
export let label
|
||||||
export let placeholder
|
export let placeholder
|
||||||
export let disabled = false
|
export let disabled = false
|
||||||
|
export let readonly = false
|
||||||
export let validation
|
export let validation
|
||||||
export let defaultValue
|
export let defaultValue
|
||||||
export let optionsSource = "schema"
|
export let optionsSource = "schema"
|
||||||
|
@ -17,6 +18,7 @@
|
||||||
export let onChange
|
export let onChange
|
||||||
export let optionsType = "select"
|
export let optionsType = "select"
|
||||||
export let direction = "vertical"
|
export let direction = "vertical"
|
||||||
|
export let span
|
||||||
|
|
||||||
let fieldState
|
let fieldState
|
||||||
let fieldApi
|
let fieldApi
|
||||||
|
@ -55,7 +57,9 @@
|
||||||
{field}
|
{field}
|
||||||
{label}
|
{label}
|
||||||
{disabled}
|
{disabled}
|
||||||
|
{readonly}
|
||||||
{validation}
|
{validation}
|
||||||
|
{span}
|
||||||
defaultValue={expandedDefaultValue}
|
defaultValue={expandedDefaultValue}
|
||||||
type="array"
|
type="array"
|
||||||
bind:fieldState
|
bind:fieldState
|
||||||
|
@ -71,6 +75,7 @@
|
||||||
getOptionValue={flatOptions ? x => x : x => x.value}
|
getOptionValue={flatOptions ? x => x : x => x.value}
|
||||||
id={fieldState.fieldId}
|
id={fieldState.fieldId}
|
||||||
disabled={fieldState.disabled}
|
disabled={fieldState.disabled}
|
||||||
|
readonly={fieldState.readonly}
|
||||||
on:change={handleChange}
|
on:change={handleChange}
|
||||||
{placeholder}
|
{placeholder}
|
||||||
{options}
|
{options}
|
||||||
|
@ -81,6 +86,7 @@
|
||||||
value={fieldState.value || []}
|
value={fieldState.value || []}
|
||||||
id={fieldState.fieldId}
|
id={fieldState.fieldId}
|
||||||
disabled={fieldState.disabled}
|
disabled={fieldState.disabled}
|
||||||
|
readonly={fieldState.readonly}
|
||||||
error={fieldState.error}
|
error={fieldState.error}
|
||||||
{options}
|
{options}
|
||||||
{direction}
|
{direction}
|
||||||
|
|
|
@ -6,6 +6,7 @@
|
||||||
export let label
|
export let label
|
||||||
export let placeholder
|
export let placeholder
|
||||||
export let disabled = false
|
export let disabled = false
|
||||||
|
export let readonly = false
|
||||||
export let optionsType = "select"
|
export let optionsType = "select"
|
||||||
export let validation
|
export let validation
|
||||||
export let defaultValue
|
export let defaultValue
|
||||||
|
@ -18,6 +19,7 @@
|
||||||
export let direction = "vertical"
|
export let direction = "vertical"
|
||||||
export let onChange
|
export let onChange
|
||||||
export let sort = true
|
export let sort = true
|
||||||
|
export let span
|
||||||
|
|
||||||
let fieldState
|
let fieldState
|
||||||
let fieldApi
|
let fieldApi
|
||||||
|
@ -45,8 +47,10 @@
|
||||||
{field}
|
{field}
|
||||||
{label}
|
{label}
|
||||||
{disabled}
|
{disabled}
|
||||||
|
{readonly}
|
||||||
{validation}
|
{validation}
|
||||||
{defaultValue}
|
{defaultValue}
|
||||||
|
{span}
|
||||||
type="options"
|
type="options"
|
||||||
bind:fieldState
|
bind:fieldState
|
||||||
bind:fieldApi
|
bind:fieldApi
|
||||||
|
@ -58,6 +62,7 @@
|
||||||
value={fieldState.value}
|
value={fieldState.value}
|
||||||
id={fieldState.fieldId}
|
id={fieldState.fieldId}
|
||||||
disabled={fieldState.disabled}
|
disabled={fieldState.disabled}
|
||||||
|
readonly={fieldState.readonly}
|
||||||
error={fieldState.error}
|
error={fieldState.error}
|
||||||
{options}
|
{options}
|
||||||
{placeholder}
|
{placeholder}
|
||||||
|
@ -72,6 +77,7 @@
|
||||||
value={fieldState.value}
|
value={fieldState.value}
|
||||||
id={fieldState.fieldId}
|
id={fieldState.fieldId}
|
||||||
disabled={fieldState.disabled}
|
disabled={fieldState.disabled}
|
||||||
|
readonly={fieldState.readonly}
|
||||||
error={fieldState.error}
|
error={fieldState.error}
|
||||||
{options}
|
{options}
|
||||||
{direction}
|
{direction}
|
||||||
|
|
|
@ -11,6 +11,7 @@
|
||||||
export let label
|
export let label
|
||||||
export let placeholder
|
export let placeholder
|
||||||
export let disabled = false
|
export let disabled = false
|
||||||
|
export let readonly = false
|
||||||
export let validation
|
export let validation
|
||||||
export let autocomplete = true
|
export let autocomplete = true
|
||||||
export let defaultValue
|
export let defaultValue
|
||||||
|
@ -18,6 +19,7 @@
|
||||||
export let filter
|
export let filter
|
||||||
export let datasourceType = "table"
|
export let datasourceType = "table"
|
||||||
export let primaryDisplay
|
export let primaryDisplay
|
||||||
|
export let span
|
||||||
|
|
||||||
let fieldState
|
let fieldState
|
||||||
let fieldApi
|
let fieldApi
|
||||||
|
@ -137,7 +139,9 @@
|
||||||
typeof value === "object" ? value._id : value
|
typeof value === "object" ? value._id : value
|
||||||
)
|
)
|
||||||
// Make sure field state is valid
|
// Make sure field state is valid
|
||||||
fieldApi.setValue(values)
|
if (values?.length > 0) {
|
||||||
|
fieldApi.setValue(values)
|
||||||
|
}
|
||||||
return values
|
return values
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -183,9 +187,11 @@
|
||||||
{label}
|
{label}
|
||||||
{field}
|
{field}
|
||||||
{disabled}
|
{disabled}
|
||||||
|
{readonly}
|
||||||
{validation}
|
{validation}
|
||||||
defaultValue={expandedDefaultValue}
|
defaultValue={expandedDefaultValue}
|
||||||
{type}
|
{type}
|
||||||
|
{span}
|
||||||
bind:fieldState
|
bind:fieldState
|
||||||
bind:fieldApi
|
bind:fieldApi
|
||||||
bind:fieldSchema
|
bind:fieldSchema
|
||||||
|
@ -200,6 +206,7 @@
|
||||||
on:loadMore={loadMore}
|
on:loadMore={loadMore}
|
||||||
id={fieldState.fieldId}
|
id={fieldState.fieldId}
|
||||||
disabled={fieldState.disabled}
|
disabled={fieldState.disabled}
|
||||||
|
readonly={fieldState.readonly}
|
||||||
error={fieldState.error}
|
error={fieldState.error}
|
||||||
getOptionLabel={getDisplayName}
|
getOptionLabel={getDisplayName}
|
||||||
getOptionValue={option => option._id}
|
getOptionValue={option => option._id}
|
||||||
|
|
|
@ -7,10 +7,12 @@
|
||||||
export let placeholder
|
export let placeholder
|
||||||
export let type = "text"
|
export let type = "text"
|
||||||
export let disabled = false
|
export let disabled = false
|
||||||
|
export let readonly = false
|
||||||
export let validation
|
export let validation
|
||||||
export let defaultValue = ""
|
export let defaultValue = ""
|
||||||
export let align
|
export let align
|
||||||
export let onChange
|
export let onChange
|
||||||
|
export let span
|
||||||
|
|
||||||
let fieldState
|
let fieldState
|
||||||
let fieldApi
|
let fieldApi
|
||||||
|
@ -27,8 +29,10 @@
|
||||||
{label}
|
{label}
|
||||||
{field}
|
{field}
|
||||||
{disabled}
|
{disabled}
|
||||||
|
{readonly}
|
||||||
{validation}
|
{validation}
|
||||||
{defaultValue}
|
{defaultValue}
|
||||||
|
{span}
|
||||||
type={type === "number" ? "number" : "string"}
|
type={type === "number" ? "number" : "string"}
|
||||||
bind:fieldState
|
bind:fieldState
|
||||||
bind:fieldApi
|
bind:fieldApi
|
||||||
|
@ -39,6 +43,7 @@
|
||||||
value={fieldState.value}
|
value={fieldState.value}
|
||||||
on:change={handleChange}
|
on:change={handleChange}
|
||||||
disabled={fieldState.disabled}
|
disabled={fieldState.disabled}
|
||||||
|
readonly={fieldState.readonly}
|
||||||
error={fieldState.error}
|
error={fieldState.error}
|
||||||
id={fieldState.fieldId}
|
id={fieldState.fieldId}
|
||||||
{placeholder}
|
{placeholder}
|
||||||
|
|
|
@ -40,6 +40,7 @@ export const styleable = (node, styles = {}) => {
|
||||||
|
|
||||||
const componentId = newStyles.id
|
const componentId = newStyles.id
|
||||||
const customStyles = newStyles.custom || ""
|
const customStyles = newStyles.custom || ""
|
||||||
|
const { isBlock } = newStyles
|
||||||
const normalStyles = { ...baseStyles, ...newStyles.normal }
|
const normalStyles = { ...baseStyles, ...newStyles.normal }
|
||||||
const hoverStyles = {
|
const hoverStyles = {
|
||||||
...normalStyles,
|
...normalStyles,
|
||||||
|
@ -76,6 +77,9 @@ export const styleable = (node, styles = {}) => {
|
||||||
// Handler to start editing a component (if applicable) when double
|
// Handler to start editing a component (if applicable) when double
|
||||||
// clicking in the builder preview
|
// clicking in the builder preview
|
||||||
editComponent = event => {
|
editComponent = event => {
|
||||||
|
if (isBlock) {
|
||||||
|
return
|
||||||
|
}
|
||||||
if (newStyles.interactive && newStyles.editable) {
|
if (newStyles.interactive && newStyles.editable) {
|
||||||
builderStore.actions.setEditMode(true)
|
builderStore.actions.setEditMode(true)
|
||||||
}
|
}
|
||||||
|
|
|
@ -33,7 +33,7 @@ export default class UserFetch extends DataFetch {
|
||||||
let finalQuery
|
let finalQuery
|
||||||
// convert old format to new one - we now allow use of the lucene format
|
// convert old format to new one - we now allow use of the lucene format
|
||||||
const { appId, paginated, ...rest } = query
|
const { appId, paginated, ...rest } = query
|
||||||
if (!LuceneUtils.hasFilters(query) && rest.email) {
|
if (!LuceneUtils.hasFilters(query) && rest.email != null) {
|
||||||
finalQuery = { string: { email: rest.email } }
|
finalQuery = { string: { email: rest.email } }
|
||||||
} else {
|
} else {
|
||||||
finalQuery = rest
|
finalQuery = rest
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
Subproject commit bbbb7a1f9e4358ec8dc428f9a1034599c81f498d
|
Subproject commit ac7785c832285255aee0b8f5309fed950f7b598c
|
|
@ -254,7 +254,7 @@ export const exportRows = async (
|
||||||
|
|
||||||
const format = ctx.query.format
|
const format = ctx.query.format
|
||||||
|
|
||||||
const { rows, columns, query } = ctx.request.body
|
const { rows, columns, query, sort, sortOrder } = ctx.request.body
|
||||||
if (typeof format !== "string" || !exporters.isFormat(format)) {
|
if (typeof format !== "string" || !exporters.isFormat(format)) {
|
||||||
ctx.throw(
|
ctx.throw(
|
||||||
400,
|
400,
|
||||||
|
@ -272,6 +272,8 @@ export const exportRows = async (
|
||||||
rowIds: rows,
|
rowIds: rows,
|
||||||
columns,
|
columns,
|
||||||
query,
|
query,
|
||||||
|
sort,
|
||||||
|
sortOrder,
|
||||||
})
|
})
|
||||||
ctx.attachment(fileName)
|
ctx.attachment(fileName)
|
||||||
return apiFileReturn(content)
|
return apiFileReturn(content)
|
||||||
|
|
|
@ -15,6 +15,16 @@ import env from "../../../environment"
|
||||||
const Router = require("@koa/router")
|
const Router = require("@koa/router")
|
||||||
const { RateLimit, Stores } = require("koa2-ratelimit")
|
const { RateLimit, Stores } = require("koa2-ratelimit")
|
||||||
import { middleware, redis } from "@budibase/backend-core"
|
import { middleware, redis } from "@budibase/backend-core"
|
||||||
|
import { SelectableDatabase } from "@budibase/backend-core/src/redis/utils"
|
||||||
|
|
||||||
|
interface KoaRateLimitOptions {
|
||||||
|
socket: {
|
||||||
|
host: string
|
||||||
|
port: number
|
||||||
|
}
|
||||||
|
password?: string
|
||||||
|
database?: number
|
||||||
|
}
|
||||||
|
|
||||||
const PREFIX = "/api/public/v1"
|
const PREFIX = "/api/public/v1"
|
||||||
// allow a lot more requests when in test
|
// allow a lot more requests when in test
|
||||||
|
@ -29,32 +39,21 @@ function getApiLimitPerSecond(): number {
|
||||||
|
|
||||||
let rateLimitStore: any = null
|
let rateLimitStore: any = null
|
||||||
if (!env.isTest()) {
|
if (!env.isTest()) {
|
||||||
const REDIS_OPTS = redis.utils.getRedisOptions()
|
const { password, host, port } = redis.utils.getRedisConnectionDetails()
|
||||||
let options
|
let options: KoaRateLimitOptions = {
|
||||||
if (REDIS_OPTS.redisProtocolUrl) {
|
socket: {
|
||||||
// fully qualified redis URL
|
host: host,
|
||||||
options = {
|
port: port,
|
||||||
url: REDIS_OPTS.redisProtocolUrl,
|
},
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
options = {
|
|
||||||
socket: {
|
|
||||||
host: REDIS_OPTS.host,
|
|
||||||
port: REDIS_OPTS.port,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
if (REDIS_OPTS.opts?.password || REDIS_OPTS.opts.redisOptions?.password) {
|
if (password) {
|
||||||
// @ts-ignore
|
options.password = password
|
||||||
options.password =
|
}
|
||||||
REDIS_OPTS.opts.password || REDIS_OPTS.opts.redisOptions.password
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!env.REDIS_CLUSTERED) {
|
if (!env.REDIS_CLUSTERED) {
|
||||||
// @ts-ignore
|
// Can't set direct redis db in clustered env
|
||||||
// Can't set direct redis db in clustered env
|
options.database = SelectableDatabase.RATE_LIMITING
|
||||||
options.database = 1
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
rateLimitStore = new Stores.Redis(options)
|
rateLimitStore = new Stores.Redis(options)
|
||||||
RateLimit.defaultOptions({
|
RateLimit.defaultOptions({
|
||||||
|
|
|
@ -563,6 +563,56 @@ describe.each([
|
||||||
await assertRowUsage(rowUsage)
|
await assertRowUsage(rowUsage)
|
||||||
await assertQueryUsage(queryUsage)
|
await assertQueryUsage(queryUsage)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it("should not overwrite links if those links are not set", async () => {
|
||||||
|
let linkField: FieldSchema = {
|
||||||
|
type: FieldType.LINK,
|
||||||
|
name: "",
|
||||||
|
fieldName: "",
|
||||||
|
constraints: {
|
||||||
|
type: "array",
|
||||||
|
presence: false,
|
||||||
|
},
|
||||||
|
relationshipType: RelationshipType.ONE_TO_MANY,
|
||||||
|
tableId: InternalTable.USER_METADATA,
|
||||||
|
}
|
||||||
|
|
||||||
|
let table = await config.api.table.create({
|
||||||
|
name: "TestTable",
|
||||||
|
type: "table",
|
||||||
|
sourceType: TableSourceType.INTERNAL,
|
||||||
|
sourceId: INTERNAL_TABLE_SOURCE_ID,
|
||||||
|
schema: {
|
||||||
|
user1: { ...linkField, name: "user1", fieldName: "user1" },
|
||||||
|
user2: { ...linkField, name: "user2", fieldName: "user2" },
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
let user1 = await config.createUser()
|
||||||
|
let user2 = await config.createUser()
|
||||||
|
|
||||||
|
let row = await config.api.row.save(table._id!, {
|
||||||
|
user1: [{ _id: user1._id }],
|
||||||
|
user2: [{ _id: user2._id }],
|
||||||
|
})
|
||||||
|
|
||||||
|
let getResp = await config.api.row.get(table._id!, row._id!)
|
||||||
|
expect(getResp.body.user1[0]._id).toEqual(user1._id)
|
||||||
|
expect(getResp.body.user2[0]._id).toEqual(user2._id)
|
||||||
|
|
||||||
|
let patchResp = await config.api.row.patch(table._id!, {
|
||||||
|
_id: row._id!,
|
||||||
|
_rev: row._rev!,
|
||||||
|
tableId: table._id!,
|
||||||
|
user1: [{ _id: user2._id }],
|
||||||
|
})
|
||||||
|
expect(patchResp.user1[0]._id).toEqual(user2._id)
|
||||||
|
expect(patchResp.user2[0]._id).toEqual(user2._id)
|
||||||
|
|
||||||
|
getResp = await config.api.row.get(table._id!, row._id!)
|
||||||
|
expect(getResp.body.user1[0]._id).toEqual(user2._id)
|
||||||
|
expect(getResp.body.user2[0]._id).toEqual(user2._id)
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
describe("destroy", () => {
|
describe("destroy", () => {
|
||||||
|
|
|
@ -36,7 +36,7 @@ describe("Run through some parts of the automations system", () => {
|
||||||
it("should be able to init in builder", async () => {
|
it("should be able to init in builder", async () => {
|
||||||
const automation: Automation = {
|
const automation: Automation = {
|
||||||
...basicAutomation(),
|
...basicAutomation(),
|
||||||
appId: config.appId,
|
appId: config.appId!,
|
||||||
}
|
}
|
||||||
const fields: any = { a: 1, appId: config.appId }
|
const fields: any = { a: 1, appId: config.appId }
|
||||||
await triggers.externalTrigger(automation, fields)
|
await triggers.externalTrigger(automation, fields)
|
||||||
|
|
|
@ -1,44 +0,0 @@
|
||||||
const setup = require("./utilities")
|
|
||||||
|
|
||||||
describe("test the update row action", () => {
|
|
||||||
let table, row, inputs
|
|
||||||
let config = setup.getConfig()
|
|
||||||
|
|
||||||
beforeAll(async () => {
|
|
||||||
await config.init()
|
|
||||||
table = await config.createTable()
|
|
||||||
row = await config.createRow()
|
|
||||||
inputs = {
|
|
||||||
rowId: row._id,
|
|
||||||
row: {
|
|
||||||
...row,
|
|
||||||
name: "Updated name",
|
|
||||||
// put a falsy option in to be removed
|
|
||||||
description: "",
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
afterAll(setup.afterAll)
|
|
||||||
|
|
||||||
it("should be able to run the action", async () => {
|
|
||||||
const res = await setup.runStep(setup.actions.UPDATE_ROW.stepId, inputs)
|
|
||||||
expect(res.success).toEqual(true)
|
|
||||||
const updatedRow = await config.getRow(table._id, res.id)
|
|
||||||
expect(updatedRow.name).toEqual("Updated name")
|
|
||||||
expect(updatedRow.description).not.toEqual("")
|
|
||||||
})
|
|
||||||
|
|
||||||
it("should check invalid inputs return an error", async () => {
|
|
||||||
const res = await setup.runStep(setup.actions.UPDATE_ROW.stepId, {})
|
|
||||||
expect(res.success).toEqual(false)
|
|
||||||
})
|
|
||||||
|
|
||||||
it("should return an error when table doesn't exist", async () => {
|
|
||||||
const res = await setup.runStep(setup.actions.UPDATE_ROW.stepId, {
|
|
||||||
row: { _id: "invalid" },
|
|
||||||
rowId: "invalid",
|
|
||||||
})
|
|
||||||
expect(res.success).toEqual(false)
|
|
||||||
})
|
|
||||||
})
|
|
|
@ -0,0 +1,169 @@
|
||||||
|
import {
|
||||||
|
FieldSchema,
|
||||||
|
FieldType,
|
||||||
|
INTERNAL_TABLE_SOURCE_ID,
|
||||||
|
InternalTable,
|
||||||
|
RelationshipType,
|
||||||
|
Row,
|
||||||
|
Table,
|
||||||
|
TableSourceType,
|
||||||
|
} from "@budibase/types"
|
||||||
|
|
||||||
|
import * as setup from "./utilities"
|
||||||
|
import * as uuid from "uuid"
|
||||||
|
|
||||||
|
describe("test the update row action", () => {
|
||||||
|
let table: Table, row: Row, inputs: any
|
||||||
|
let config = setup.getConfig()
|
||||||
|
|
||||||
|
beforeAll(async () => {
|
||||||
|
await config.init()
|
||||||
|
table = await config.createTable()
|
||||||
|
row = await config.createRow()
|
||||||
|
inputs = {
|
||||||
|
rowId: row._id,
|
||||||
|
row: {
|
||||||
|
...row,
|
||||||
|
name: "Updated name",
|
||||||
|
// put a falsy option in to be removed
|
||||||
|
description: "",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
afterAll(setup.afterAll)
|
||||||
|
|
||||||
|
it("should be able to run the action", async () => {
|
||||||
|
const res = await setup.runStep(setup.actions.UPDATE_ROW.stepId, inputs)
|
||||||
|
expect(res.success).toEqual(true)
|
||||||
|
const updatedRow = await config.getRow(table._id!, res.id)
|
||||||
|
expect(updatedRow.name).toEqual("Updated name")
|
||||||
|
expect(updatedRow.description).not.toEqual("")
|
||||||
|
})
|
||||||
|
|
||||||
|
it("should check invalid inputs return an error", async () => {
|
||||||
|
const res = await setup.runStep(setup.actions.UPDATE_ROW.stepId, {})
|
||||||
|
expect(res.success).toEqual(false)
|
||||||
|
})
|
||||||
|
|
||||||
|
it("should return an error when table doesn't exist", async () => {
|
||||||
|
const res = await setup.runStep(setup.actions.UPDATE_ROW.stepId, {
|
||||||
|
row: { _id: "invalid" },
|
||||||
|
rowId: "invalid",
|
||||||
|
})
|
||||||
|
expect(res.success).toEqual(false)
|
||||||
|
})
|
||||||
|
|
||||||
|
it("should not overwrite links if those links are not set", async () => {
|
||||||
|
let linkField: FieldSchema = {
|
||||||
|
type: FieldType.LINK,
|
||||||
|
name: "",
|
||||||
|
fieldName: "",
|
||||||
|
constraints: {
|
||||||
|
type: "array",
|
||||||
|
presence: false,
|
||||||
|
},
|
||||||
|
relationshipType: RelationshipType.ONE_TO_MANY,
|
||||||
|
tableId: InternalTable.USER_METADATA,
|
||||||
|
}
|
||||||
|
|
||||||
|
let table = await config.api.table.create({
|
||||||
|
name: uuid.v4(),
|
||||||
|
type: "table",
|
||||||
|
sourceType: TableSourceType.INTERNAL,
|
||||||
|
sourceId: INTERNAL_TABLE_SOURCE_ID,
|
||||||
|
schema: {
|
||||||
|
user1: { ...linkField, name: "user1", fieldName: uuid.v4() },
|
||||||
|
user2: { ...linkField, name: "user2", fieldName: uuid.v4() },
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
let user1 = await config.createUser()
|
||||||
|
let user2 = await config.createUser()
|
||||||
|
|
||||||
|
let row = await config.api.row.save(table._id!, {
|
||||||
|
user1: [{ _id: user1._id }],
|
||||||
|
user2: [{ _id: user2._id }],
|
||||||
|
})
|
||||||
|
|
||||||
|
let getResp = await config.api.row.get(table._id!, row._id!)
|
||||||
|
expect(getResp.body.user1[0]._id).toEqual(user1._id)
|
||||||
|
expect(getResp.body.user2[0]._id).toEqual(user2._id)
|
||||||
|
|
||||||
|
let stepResp = await setup.runStep(setup.actions.UPDATE_ROW.stepId, {
|
||||||
|
rowId: row._id,
|
||||||
|
row: {
|
||||||
|
_id: row._id,
|
||||||
|
_rev: row._rev,
|
||||||
|
tableId: row.tableId,
|
||||||
|
user1: [user2._id],
|
||||||
|
user2: "",
|
||||||
|
},
|
||||||
|
})
|
||||||
|
expect(stepResp.success).toEqual(true)
|
||||||
|
|
||||||
|
getResp = await config.api.row.get(table._id!, row._id!)
|
||||||
|
expect(getResp.body.user1[0]._id).toEqual(user2._id)
|
||||||
|
expect(getResp.body.user2[0]._id).toEqual(user2._id)
|
||||||
|
})
|
||||||
|
|
||||||
|
it("should overwrite links if those links are not set and we ask it do", async () => {
|
||||||
|
let linkField: FieldSchema = {
|
||||||
|
type: FieldType.LINK,
|
||||||
|
name: "",
|
||||||
|
fieldName: "",
|
||||||
|
constraints: {
|
||||||
|
type: "array",
|
||||||
|
presence: false,
|
||||||
|
},
|
||||||
|
relationshipType: RelationshipType.ONE_TO_MANY,
|
||||||
|
tableId: InternalTable.USER_METADATA,
|
||||||
|
}
|
||||||
|
|
||||||
|
let table = await config.api.table.create({
|
||||||
|
name: uuid.v4(),
|
||||||
|
type: "table",
|
||||||
|
sourceType: TableSourceType.INTERNAL,
|
||||||
|
sourceId: INTERNAL_TABLE_SOURCE_ID,
|
||||||
|
schema: {
|
||||||
|
user1: { ...linkField, name: "user1", fieldName: uuid.v4() },
|
||||||
|
user2: { ...linkField, name: "user2", fieldName: uuid.v4() },
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
let user1 = await config.createUser()
|
||||||
|
let user2 = await config.createUser()
|
||||||
|
|
||||||
|
let row = await config.api.row.save(table._id!, {
|
||||||
|
user1: [{ _id: user1._id }],
|
||||||
|
user2: [{ _id: user2._id }],
|
||||||
|
})
|
||||||
|
|
||||||
|
let getResp = await config.api.row.get(table._id!, row._id!)
|
||||||
|
expect(getResp.body.user1[0]._id).toEqual(user1._id)
|
||||||
|
expect(getResp.body.user2[0]._id).toEqual(user2._id)
|
||||||
|
|
||||||
|
let stepResp = await setup.runStep(setup.actions.UPDATE_ROW.stepId, {
|
||||||
|
rowId: row._id,
|
||||||
|
row: {
|
||||||
|
_id: row._id,
|
||||||
|
_rev: row._rev,
|
||||||
|
tableId: row.tableId,
|
||||||
|
user1: [user2._id],
|
||||||
|
user2: "",
|
||||||
|
},
|
||||||
|
meta: {
|
||||||
|
fields: {
|
||||||
|
user2: {
|
||||||
|
clearRelationships: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
expect(stepResp.success).toEqual(true)
|
||||||
|
|
||||||
|
getResp = await config.api.row.get(table._id!, row._id!)
|
||||||
|
expect(getResp.body.user1[0]._id).toEqual(user2._id)
|
||||||
|
expect(getResp.body.user2).toBeUndefined()
|
||||||
|
})
|
||||||
|
})
|
|
@ -4,11 +4,11 @@ import { BUILTIN_ACTION_DEFINITIONS, getAction } from "../../actions"
|
||||||
import emitter from "../../../events/index"
|
import emitter from "../../../events/index"
|
||||||
import env from "../../../environment"
|
import env from "../../../environment"
|
||||||
|
|
||||||
let config: any
|
let config: TestConfig
|
||||||
|
|
||||||
export function getConfig() {
|
export function getConfig(): TestConfig {
|
||||||
if (!config) {
|
if (!config) {
|
||||||
config = new TestConfig(false)
|
config = new TestConfig(true)
|
||||||
}
|
}
|
||||||
return config
|
return config
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,10 @@
|
||||||
import { Row, SearchFilters, SearchParams } from "@budibase/types"
|
import {
|
||||||
|
Row,
|
||||||
|
SearchFilters,
|
||||||
|
SearchParams,
|
||||||
|
SortOrder,
|
||||||
|
SortType,
|
||||||
|
} from "@budibase/types"
|
||||||
import { isExternalTableID } from "../../../integrations/utils"
|
import { isExternalTableID } from "../../../integrations/utils"
|
||||||
import * as internal from "./search/internal"
|
import * as internal from "./search/internal"
|
||||||
import * as external from "./search/external"
|
import * as external from "./search/external"
|
||||||
|
@ -32,6 +38,8 @@ export interface ExportRowsParams {
|
||||||
rowIds?: string[]
|
rowIds?: string[]
|
||||||
columns?: string[]
|
columns?: string[]
|
||||||
query?: SearchFilters
|
query?: SearchFilters
|
||||||
|
sort?: string
|
||||||
|
sortOrder?: SortOrder
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ExportRowsResult {
|
export interface ExportRowsResult {
|
||||||
|
|
|
@ -98,12 +98,12 @@ export async function search(options: SearchParams) {
|
||||||
export async function exportRows(
|
export async function exportRows(
|
||||||
options: ExportRowsParams
|
options: ExportRowsParams
|
||||||
): Promise<ExportRowsResult> {
|
): Promise<ExportRowsResult> {
|
||||||
const { tableId, format, columns, rowIds } = options
|
const { tableId, format, columns, rowIds, query, sort, sortOrder } = options
|
||||||
const { datasourceId, tableName } = breakExternalTableId(tableId)
|
const { datasourceId, tableName } = breakExternalTableId(tableId)
|
||||||
|
|
||||||
let query: SearchFilters = {}
|
let requestQuery: SearchFilters = {}
|
||||||
if (rowIds?.length) {
|
if (rowIds?.length) {
|
||||||
query = {
|
requestQuery = {
|
||||||
oneOf: {
|
oneOf: {
|
||||||
_id: rowIds.map((row: string) => {
|
_id: rowIds.map((row: string) => {
|
||||||
const ids = JSON.parse(
|
const ids = JSON.parse(
|
||||||
|
@ -119,6 +119,8 @@ export async function exportRows(
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
requestQuery = query || {}
|
||||||
}
|
}
|
||||||
|
|
||||||
const datasource = await sdk.datasources.get(datasourceId!)
|
const datasource = await sdk.datasources.get(datasourceId!)
|
||||||
|
@ -126,7 +128,7 @@ export async function exportRows(
|
||||||
throw new HTTPError("Datasource has not been configured for plus API.", 400)
|
throw new HTTPError("Datasource has not been configured for plus API.", 400)
|
||||||
}
|
}
|
||||||
|
|
||||||
let result = await search({ tableId, query })
|
let result = await search({ tableId, query: requestQuery, sort, sortOrder })
|
||||||
let rows: Row[] = []
|
let rows: Row[] = []
|
||||||
|
|
||||||
// Filter data to only specified columns if required
|
// Filter data to only specified columns if required
|
||||||
|
|
|
@ -84,7 +84,7 @@ export async function search(options: SearchParams) {
|
||||||
export async function exportRows(
|
export async function exportRows(
|
||||||
options: ExportRowsParams
|
options: ExportRowsParams
|
||||||
): Promise<ExportRowsResult> {
|
): Promise<ExportRowsResult> {
|
||||||
const { tableId, format, rowIds, columns, query } = options
|
const { tableId, format, rowIds, columns, query, sort, sortOrder } = options
|
||||||
const db = context.getAppDB()
|
const db = context.getAppDB()
|
||||||
const table = await sdk.tables.getTable(tableId)
|
const table = await sdk.tables.getTable(tableId)
|
||||||
|
|
||||||
|
@ -99,7 +99,12 @@ export async function exportRows(
|
||||||
|
|
||||||
result = await outputProcessing(table, response)
|
result = await outputProcessing(table, response)
|
||||||
} else if (query) {
|
} else if (query) {
|
||||||
let searchResponse = await search({ tableId, query })
|
let searchResponse = await search({
|
||||||
|
tableId,
|
||||||
|
query,
|
||||||
|
sort,
|
||||||
|
sortOrder,
|
||||||
|
})
|
||||||
result = searchResponse.rows
|
result = searchResponse.rows
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -22,7 +22,15 @@ export class TableAPI extends TestAPI {
|
||||||
.send(data)
|
.send(data)
|
||||||
.set(this.config.defaultHeaders())
|
.set(this.config.defaultHeaders())
|
||||||
.expect("Content-Type", /json/)
|
.expect("Content-Type", /json/)
|
||||||
.expect(expectStatus)
|
|
||||||
|
if (res.status !== expectStatus) {
|
||||||
|
throw new Error(
|
||||||
|
`Expected status ${expectStatus} but got ${
|
||||||
|
res.status
|
||||||
|
} with body ${JSON.stringify(res.body)}`
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
return res.body
|
return res.body
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
import { SearchFilters, SearchParams } from "../../../sdk"
|
import { SearchFilters, SearchParams } from "../../../sdk"
|
||||||
import { Row } from "../../../documents"
|
import { Row } from "../../../documents"
|
||||||
|
import { SortOrder } from "../../../api"
|
||||||
import { ReadStream } from "fs"
|
import { ReadStream } from "fs"
|
||||||
|
|
||||||
export interface SaveRowRequest extends Row {}
|
export interface SaveRowRequest extends Row {}
|
||||||
|
@ -34,6 +35,8 @@ export interface ExportRowsRequest {
|
||||||
rows: string[]
|
rows: string[]
|
||||||
columns?: string[]
|
columns?: string[]
|
||||||
query?: SearchFilters
|
query?: SearchFilters
|
||||||
|
sort?: string
|
||||||
|
sortOrder?: SortOrder
|
||||||
}
|
}
|
||||||
|
|
||||||
export type ExportRowsResponse = ReadStream
|
export type ExportRowsResponse = ReadStream
|
||||||
|
|
|
@ -102,6 +102,7 @@ export interface BBReferenceFieldMetadata
|
||||||
extends Omit<BaseFieldSchema, "subtype"> {
|
extends Omit<BaseFieldSchema, "subtype"> {
|
||||||
type: FieldType.BB_REFERENCE
|
type: FieldType.BB_REFERENCE
|
||||||
subtype: FieldSubtype.USER | FieldSubtype.USERS
|
subtype: FieldSubtype.USER | FieldSubtype.USERS
|
||||||
|
relationshipType?: RelationshipType
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface FieldConstraints {
|
export interface FieldConstraints {
|
||||||
|
|
|
@ -31,10 +31,6 @@ import destroyable from "server-destroy"
|
||||||
import { initPro } from "./initPro"
|
import { initPro } from "./initPro"
|
||||||
import { handleScimBody } from "./middleware/handleScimBody"
|
import { handleScimBody } from "./middleware/handleScimBody"
|
||||||
|
|
||||||
// configure events to use the pro audit log write
|
|
||||||
// can't integrate directly into backend-core due to cyclic issues
|
|
||||||
events.processors.init(proSdk.auditLogs.write)
|
|
||||||
|
|
||||||
if (coreEnv.ENABLE_SSO_MAINTENANCE_MODE) {
|
if (coreEnv.ENABLE_SSO_MAINTENANCE_MODE) {
|
||||||
console.warn(
|
console.warn(
|
||||||
"Warning: ENABLE_SSO_MAINTENANCE_MODE is set. It is recommended this flag is disabled if maintenance is not in progress"
|
"Warning: ENABLE_SSO_MAINTENANCE_MODE is set. It is recommended this flag is disabled if maintenance is not in progress"
|
||||||
|
@ -93,6 +89,9 @@ export default server.listen(parseInt(env.PORT || "4002"), async () => {
|
||||||
console.log(`Worker running on ${JSON.stringify(server.address())}`)
|
console.log(`Worker running on ${JSON.stringify(server.address())}`)
|
||||||
await initPro()
|
await initPro()
|
||||||
await redis.init()
|
await redis.init()
|
||||||
|
// configure events to use the pro audit log write
|
||||||
|
// can't integrate directly into backend-core due to cyclic issues
|
||||||
|
await events.processors.init(proSdk.auditLogs.write)
|
||||||
})
|
})
|
||||||
|
|
||||||
process.on("uncaughtException", err => {
|
process.on("uncaughtException", err => {
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
## Description
|
## Description
|
||||||
_Describe the problem or feature in addition to a link to the relevant github issues._
|
_Describe the problem or feature in addition to a link to the relevant github issues._
|
||||||
|
|
||||||
### Addresses:
|
## Addresses
|
||||||
- `<Enter the Link to the issue(s) this PR addresses>`
|
- `<Enter the Link to the issue(s) this PR addresses>`
|
||||||
- ...more if required
|
- ...more if required
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue