diff --git a/.eslintrc.json b/.eslintrc.json index 75584b8163..f6f03c6523 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -19,6 +19,7 @@ "bundle.js" ], "extends": ["eslint:recommended"], + "plugins": ["import", "eslint-plugin-local-rules"], "overrides": [ { "files": ["**/*.svelte"], @@ -30,7 +31,6 @@ "sourceType": "module", "allowImportExportEverywhere": true } - }, { "files": ["**/*.ts"], @@ -42,13 +42,22 @@ "no-case-declarations": "off", "no-useless-escape": "off", "no-undef": "off", - "no-prototype-builtins": "off" + "no-prototype-builtins": "off", + "local-rules/no-budibase-imports": "error" } } ], "rules": { "no-self-assign": "off", - "no-unused-vars": ["error", { "varsIgnorePattern": "^_", "argsIgnorePattern": "^_", "destructuredArrayIgnorePattern": "^_" }] + "no-unused-vars": [ + "error", + { + "varsIgnorePattern": "^_", + "argsIgnorePattern": "^_", + "destructuredArrayIgnorePattern": "^_" + } + ], + "import/no-relative-packages": "error" }, "globals": { "GeolocationPositionError": true diff --git a/.github/workflows/budibase_ci.yml b/.github/workflows/budibase_ci.yml index 77867c8617..7929da12e7 100644 --- a/.github/workflows/budibase_ci.yml +++ b/.github/workflows/budibase_ci.yml @@ -19,50 +19,41 @@ env: PERSONAL_ACCESS_TOKEN: ${{ secrets.PERSONAL_ACCESS_TOKEN }} NX_BASE_BRANCH: origin/${{ github.base_ref }} USE_NX_AFFECTED: ${{ github.event_name == 'pull_request' }} + IS_OSS_CONTRIBUTOR: ${{ github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name != 'Budibase/budibase' }} jobs: lint: runs-on: ubuntu-latest steps: - - name: Checkout repo and submodules + - name: Checkout repo uses: actions/checkout@v3 - if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == 'Budibase/budibase' with: - submodules: true + submodules: ${{ env.IS_OSS_CONTRIBUTOR == 'false' }} token: ${{ secrets.PERSONAL_ACCESS_TOKEN || github.token }} - - name: Checkout repo only - uses: actions/checkout@v3 - if: github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name != 'Budibase/budibase' - name: Use Node.js 18.x uses: actions/setup-node@v3 with: node-version: 18.x - cache: "yarn" + cache: yarn - run: yarn --frozen-lockfile - run: yarn lint build: runs-on: ubuntu-latest steps: - - name: Checkout repo and submodules + - name: Checkout repo uses: actions/checkout@v3 - if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == 'Budibase/budibase' with: - submodules: true + submodules: ${{ env.IS_OSS_CONTRIBUTOR == 'false' }} token: ${{ secrets.PERSONAL_ACCESS_TOKEN || github.token }} fetch-depth: 0 - - name: Checkout repo only - uses: actions/checkout@v3 - if: github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name != 'Budibase/budibase' - with: - fetch-depth: 0 - name: Use Node.js 18.x uses: actions/setup-node@v3 with: node-version: 18.x - cache: "yarn" + cache: yarn - run: yarn --frozen-lockfile # Run build all the projects @@ -81,24 +72,18 @@ jobs: test-libraries: runs-on: ubuntu-latest steps: - - name: Checkout repo and submodules + - name: Checkout repo uses: actions/checkout@v3 - if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == 'Budibase/budibase' with: - submodules: true + submodules: ${{ env.IS_OSS_CONTRIBUTOR == 'false' }} token: ${{ secrets.PERSONAL_ACCESS_TOKEN || github.token }} fetch-depth: 0 - - name: Checkout repo only - uses: actions/checkout@v3 - if: github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name != 'Budibase/budibase' - with: - fetch-depth: 0 - name: Use Node.js 18.x uses: actions/setup-node@v3 with: node-version: 18.x - cache: "yarn" + cache: yarn - run: yarn --frozen-lockfile - name: Test run: | @@ -116,24 +101,18 @@ jobs: test-worker: runs-on: ubuntu-latest steps: - - name: Checkout repo and submodules + - name: Checkout repo uses: actions/checkout@v3 - if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == 'Budibase/budibase' with: - submodules: true + submodules: ${{ env.IS_OSS_CONTRIBUTOR == 'false' }} token: ${{ secrets.PERSONAL_ACCESS_TOKEN || github.token }} fetch-depth: 0 - - name: Checkout repo only - uses: actions/checkout@v3 - if: github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name != 'Budibase/budibase' - with: - fetch-depth: 0 - name: Use Node.js 18.x uses: actions/setup-node@v3 with: node-version: 18.x - cache: "yarn" + cache: yarn - run: yarn --frozen-lockfile - name: Test worker run: | @@ -152,24 +131,18 @@ jobs: test-server: runs-on: ubuntu-latest steps: - - name: Checkout repo and submodules + - name: Checkout repo uses: actions/checkout@v3 - if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == 'Budibase/budibase' with: - submodules: true + submodules: ${{ env.IS_OSS_CONTRIBUTOR == 'false' }} token: ${{ secrets.PERSONAL_ACCESS_TOKEN || github.token }} fetch-depth: 0 - - name: Checkout repo only - uses: actions/checkout@v3 - if: github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name != 'Budibase/budibase' - with: - fetch-depth: 0 - name: Use Node.js 18.x uses: actions/setup-node@v3 with: node-version: 18.x - cache: "yarn" + cache: yarn - run: yarn --frozen-lockfile - name: Test server run: | @@ -200,7 +173,7 @@ jobs: uses: actions/setup-node@v3 with: node-version: 18.x - cache: "yarn" + cache: yarn - run: yarn --frozen-lockfile - name: Test run: | @@ -213,24 +186,23 @@ jobs: integration-test: runs-on: ubuntu-latest steps: - - name: Checkout repo and submodules + - name: Checkout repo uses: actions/checkout@v3 - if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == 'Budibase/budibase' with: - submodules: true + submodules: ${{ env.IS_OSS_CONTRIBUTOR == 'false' }} token: ${{ secrets.PERSONAL_ACCESS_TOKEN || github.token }} - - name: Checkout repo only - uses: actions/checkout@v3 - if: github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name != 'Budibase/budibase' - name: Use Node.js 18.x uses: actions/setup-node@v3 with: node-version: 18.x - cache: "yarn" + cache: yarn - run: yarn --frozen-lockfile - name: Build packages run: yarn build --scope @budibase/server --scope @budibase/worker + - name: Build backend-core for OSS contributor (required for pro) + if: ${{ env.IS_OSS_CONTRIBUTOR == 'true' }} + run: yarn build --scope @budibase/backend-core - name: Run tests run: | cd qa-core diff --git a/eslint-local-rules/index.js b/eslint-local-rules/index.js new file mode 100644 index 0000000000..af02599c90 --- /dev/null +++ b/eslint-local-rules/index.js @@ -0,0 +1,21 @@ +module.exports = { + "no-budibase-imports": { + create: function (context) { + return { + ImportDeclaration(node) { + const importPath = node.source.value + + if ( + /^@budibase\/[^/]+\/.*$/.test(importPath) && + importPath !== "@budibase/backend-core/tests" + ) { + context.report({ + node, + message: `Importing from @budibase is not allowed, except for @budibase/backend-core/tests.`, + }) + } + }, + } + }, + }, +} diff --git a/lerna.json b/lerna.json index ce3128165f..a12b1238b3 100644 --- a/lerna.json +++ b/lerna.json @@ -1,5 +1,5 @@ { - "version": "2.13.6", + "version": "2.13.10", "npmClient": "yarn", "packages": [ "packages/*" diff --git a/package.json b/package.json index 8a27cde104..2978483448 100644 --- a/package.json +++ b/package.json @@ -2,11 +2,17 @@ "name": "root", "private": true, "devDependencies": { + "@babel/core": "^7.22.5", + "@babel/eslint-parser": "^7.22.5", + "@babel/preset-env": "^7.22.5", "@esbuild-plugins/tsconfig-paths": "^0.1.2", "@typescript-eslint/parser": "6.7.2", "esbuild": "^0.18.17", "esbuild-node-externals": "^1.8.0", "eslint": "^8.44.0", + "eslint-plugin-import": "^2.29.0", + "eslint-plugin-local-rules": "^2.0.0", + "eslint-plugin-svelte": "^2.32.2", "husky": "^8.0.3", "kill-port": "^1.6.1", "lerna": "7.1.1", @@ -17,12 +23,8 @@ "prettier": "2.8.8", "prettier-plugin-svelte": "^2.3.0", "svelte": "3.49.0", - "typescript": "5.2.2", - "@babel/core": "^7.22.5", - "@babel/eslint-parser": "^7.22.5", - "@babel/preset-env": "^7.22.5", - "eslint-plugin-svelte": "^2.32.2", - "svelte-eslint-parser": "^0.32.0" + "svelte-eslint-parser": "^0.32.0", + "typescript": "5.2.2" }, "scripts": { "preinstall": "node scripts/syncProPackage.js", diff --git a/packages/backend-core/src/cache/generic.ts b/packages/backend-core/src/cache/generic.ts index 7cd5d6227f..7a2be5a0f0 100644 --- a/packages/backend-core/src/cache/generic.ts +++ b/packages/backend-core/src/cache/generic.ts @@ -1,6 +1,6 @@ -const BaseCache = require("./base") +import BaseCache from "./base" -const GENERIC = new BaseCache.default() +const GENERIC = new BaseCache() export enum CacheKey { CHECKLIST = "checklist", @@ -19,6 +19,7 @@ export enum TTL { } function performExport(funcName: string) { + // @ts-ignore return (...args: any) => GENERIC[funcName](...args) } diff --git a/packages/backend-core/src/cache/index.ts b/packages/backend-core/src/cache/index.ts index 58928c271a..4fa986e4e2 100644 --- a/packages/backend-core/src/cache/index.ts +++ b/packages/backend-core/src/cache/index.ts @@ -2,4 +2,6 @@ export * as generic from "./generic" export * as user from "./user" export * as app from "./appMetadata" export * as writethrough from "./writethrough" +export * as invite from "./invite" +export * as passwordReset from "./passwordReset" export * from "./generic" diff --git a/packages/backend-core/src/cache/invite.ts b/packages/backend-core/src/cache/invite.ts new file mode 100644 index 0000000000..e43ebc4aa8 --- /dev/null +++ b/packages/backend-core/src/cache/invite.ts @@ -0,0 +1,86 @@ +import * as utils from "../utils" +import { Duration, DurationType } from "../utils" +import env from "../environment" +import { getTenantId } from "../context" +import * as redis from "../redis/init" + +const TTL_SECONDS = Duration.fromDays(7).toSeconds() + +interface Invite { + email: string + info: any +} + +interface InviteWithCode extends Invite { + code: string +} + +/** + * Given an invite code and invite body, allow the update an existing/valid invite in redis + * @param code The invite code for an invite in redis + * @param value The body of the updated user invitation + */ +export async function updateCode(code: string, value: Invite) { + const client = await redis.getInviteClient() + await client.store(code, value, TTL_SECONDS) +} + +/** + * Generates an invitation code and writes it to redis - which can later be checked for user creation. + * @param email the email address which the code is being sent to (for use later). + * @param info Information to be carried along with the invitation. + * @return returns the code that was stored to redis. + */ +export async function createCode(email: string, info: any): Promise { + const code = utils.newid() + const client = await redis.getInviteClient() + await client.store(code, { email, info }, TTL_SECONDS) + return code +} + +/** + * Checks that the provided invite code is valid - will return the email address of user that was invited. + * @param code the invite code that was provided as part of the link. + * @return If the code is valid then an email address will be returned. + */ +export async function getCode(code: string): Promise { + const client = await redis.getInviteClient() + const value = (await client.get(code)) as Invite | undefined + if (!value) { + throw "Invitation is not valid or has expired, please request a new one." + } + return value +} + +export async function deleteCode(code: string) { + const client = await redis.getInviteClient() + await client.delete(code) +} + +/** + Get all currently available user invitations for the current tenant. + **/ +export async function getInviteCodes(): Promise { + const client = await redis.getInviteClient() + const invites: { key: string; value: Invite }[] = await client.scan() + + const results: InviteWithCode[] = invites.map(invite => { + return { + ...invite.value, + code: invite.key, + } + }) + if (!env.MULTI_TENANCY) { + return results + } + const tenantId = getTenantId() + return results.filter(invite => tenantId === invite.info.tenantId) +} + +export async function getExistingInvites( + emails: string[] +): Promise { + return (await getInviteCodes()).filter(invite => + emails.includes(invite.email) + ) +} diff --git a/packages/backend-core/src/cache/passwordReset.ts b/packages/backend-core/src/cache/passwordReset.ts new file mode 100644 index 0000000000..7f5a93f149 --- /dev/null +++ b/packages/backend-core/src/cache/passwordReset.ts @@ -0,0 +1,38 @@ +import * as redis from "../redis/init" +import * as utils from "../utils" +import { Duration, DurationType } from "../utils" + +const TTL_SECONDS = Duration.fromHours(1).toSeconds() + +interface PasswordReset { + userId: string + info: any +} + +/** + * Given a user ID this will store a code (that is returned) for an hour in redis. + * The user can then return this code for resetting their password (through their reset link). + * @param userId the ID of the user which is to be reset. + * @param info Info about the user/the reset process. + * @return returns the code that was stored to redis. + */ +export async function createCode(userId: string, info: any): Promise { + const code = utils.newid() + const client = await redis.getPasswordResetClient() + await client.store(code, { userId, info }, TTL_SECONDS) + return code +} + +/** + * Given a reset code this will lookup to redis, check if the code is valid. + * @param code The code provided via the email link. + * @return returns the user ID if it is found + */ +export async function getCode(code: string): Promise { + const client = await redis.getPasswordResetClient() + const value = (await client.get(code)) as PasswordReset | undefined + if (!value) { + throw "Provided information is not valid, cannot reset password - please try again." + } + return value +} diff --git a/packages/backend-core/src/db/couch/DatabaseImpl.ts b/packages/backend-core/src/db/couch/DatabaseImpl.ts index 6a1e575ac9..8588a7157a 100644 --- a/packages/backend-core/src/db/couch/DatabaseImpl.ts +++ b/packages/backend-core/src/db/couch/DatabaseImpl.ts @@ -10,6 +10,7 @@ import { DatabaseDeleteIndexOpts, Document, isDocument, + RowResponse, } from "@budibase/types" import { getCouchInfo } from "./connections" import { directCouchUrlCall } from "./utils" @@ -127,12 +128,19 @@ export class DatabaseImpl implements Database { keys: ids, include_docs: true, }) - const NOT_FOUND = "not_found" - const rows = response.rows.filter(row => row.error !== NOT_FOUND) + const rowUnavailable = (row: RowResponse) => { + // row is deleted - key lookup can return this + if (row.doc == null || ("deleted" in row.value && row.value.deleted)) { + return true + } + return row.error === "not_found" + } + + const rows = response.rows.filter(row => !rowUnavailable(row)) const someMissing = rows.length !== response.rows.length // some were filtered out - means some missing if (!opts?.allowMissing && someMissing) { - const missing = response.rows.filter(row => row.error === NOT_FOUND) + const missing = response.rows.filter(row => rowUnavailable(row)) const missingIds = missing.map(row => row.key).join(", ") throw new Error(`Unable to get documents: ${missingIds}`) } diff --git a/packages/backend-core/src/index.ts b/packages/backend-core/src/index.ts index c7cf9f56cc..2cfd517941 100644 --- a/packages/backend-core/src/index.ts +++ b/packages/backend-core/src/index.ts @@ -32,6 +32,7 @@ export * as blacklist from "./blacklist" export * as docUpdates from "./docUpdates" export * from "./utils/Duration" export { SearchParams } from "./db" +export * as docIds from "./docIds" // Add context to tenancy for backwards compatibility // only do this for external usages to prevent internal // circular dependencies @@ -50,6 +51,7 @@ export * from "./constants" // expose package init function import * as db from "./db" + export const init = (opts: any = {}) => { db.init(opts.db) } diff --git a/packages/backend-core/src/redis/init.ts b/packages/backend-core/src/redis/init.ts index 55ffe3dd12..f3bcee3209 100644 --- a/packages/backend-core/src/redis/init.ts +++ b/packages/backend-core/src/redis/init.ts @@ -7,15 +7,19 @@ let userClient: Client, cacheClient: Client, writethroughClient: Client, lockClient: Client, - socketClient: Client + socketClient: Client, + inviteClient: Client, + passwordResetClient: Client -async function init() { +export async function init() { userClient = await new Client(utils.Databases.USER_CACHE).init() sessionClient = await new Client(utils.Databases.SESSIONS).init() appClient = await new Client(utils.Databases.APP_METADATA).init() cacheClient = await new Client(utils.Databases.GENERIC_CACHE).init() lockClient = await new Client(utils.Databases.LOCKS).init() writethroughClient = await new Client(utils.Databases.WRITE_THROUGH).init() + inviteClient = await new Client(utils.Databases.INVITATIONS).init() + passwordResetClient = await new Client(utils.Databases.PW_RESETS).init() socketClient = await new Client( utils.Databases.SOCKET_IO, utils.SelectableDatabase.SOCKET_IO @@ -29,6 +33,8 @@ export async function shutdown() { if (cacheClient) await cacheClient.finish() if (writethroughClient) await writethroughClient.finish() if (lockClient) await lockClient.finish() + if (inviteClient) await inviteClient.finish() + if (passwordResetClient) await passwordResetClient.finish() if (socketClient) await socketClient.finish() } @@ -84,3 +90,17 @@ export async function getSocketClient() { } return socketClient } + +export async function getInviteClient() { + if (!inviteClient) { + await init() + } + return inviteClient +} + +export async function getPasswordResetClient() { + if (!passwordResetClient) { + await init() + } + return passwordResetClient +} diff --git a/packages/backend-core/src/redis/redis.ts b/packages/backend-core/src/redis/redis.ts index 6f1b573718..701e262091 100644 --- a/packages/backend-core/src/redis/redis.ts +++ b/packages/backend-core/src/redis/redis.ts @@ -28,7 +28,6 @@ const DEFAULT_SELECT_DB = SelectableDatabase.DEFAULT // for testing just generate the client once let CLOSED = false let CLIENTS: { [key: number]: any } = {} -0 let CONNECTED = false // mock redis always connected diff --git a/packages/backend-core/src/users/db.ts b/packages/backend-core/src/users/db.ts index 59f698d99c..bd85097bbd 100644 --- a/packages/backend-core/src/users/db.ts +++ b/packages/backend-core/src/users/db.ts @@ -303,7 +303,7 @@ export class UserDB { static async bulkCreate( newUsersRequested: User[], - groups: string[] + groups?: string[] ): Promise { const tenantId = getTenantId() @@ -328,7 +328,7 @@ export class UserDB { }) continue } - newUser.userGroups = groups + newUser.userGroups = groups || [] newUsers.push(newUser) if (isCreator(newUser)) { newCreators.push(newUser) diff --git a/packages/backend-core/src/users/lookup.ts b/packages/backend-core/src/users/lookup.ts index 17d0e91d88..355be74dab 100644 --- a/packages/backend-core/src/users/lookup.ts +++ b/packages/backend-core/src/users/lookup.ts @@ -6,6 +6,7 @@ import { } from "@budibase/types" import * as dbUtils from "../db" import { ViewName } from "../constants" +import { getExistingInvites } from "../cache/invite" /** * Apply a system-wide search on emails: @@ -26,6 +27,9 @@ export async function searchExistingEmails(emails: string[]) { const existingAccounts = await getExistingAccounts(emails) matchedEmails.push(...existingAccounts.map(account => account.email)) + const invitedEmails = await getExistingInvites(emails) + matchedEmails.push(...invitedEmails.map(invite => invite.email)) + return [...new Set(matchedEmails.map(email => email.toLowerCase()))] } diff --git a/packages/backend-core/src/utils/Duration.ts b/packages/backend-core/src/utils/Duration.ts index f376c2f7c7..3c7ef23b11 100644 --- a/packages/backend-core/src/utils/Duration.ts +++ b/packages/backend-core/src/utils/Duration.ts @@ -28,6 +28,9 @@ export class Duration { toMs: () => { return Duration.convert(from, DurationType.MILLISECONDS, duration) }, + toSeconds: () => { + return Duration.convert(from, DurationType.SECONDS, duration) + }, } } diff --git a/packages/backend-core/tests/core/utilities/structures/users.ts b/packages/backend-core/tests/core/utilities/structures/users.ts index 66d23696e0..68ee29686c 100644 --- a/packages/backend-core/tests/core/utilities/structures/users.ts +++ b/packages/backend-core/tests/core/utilities/structures/users.ts @@ -12,7 +12,7 @@ import { generator } from "./generator" import { tenant } from "." export const newEmail = () => { - return `${uuid()}@test.com` + return `${uuid()}@example.com` } export const user = (userProps?: Partial>): User => { diff --git a/packages/builder/src/components/backend/Datasources/CreateEditRelationship.svelte b/packages/builder/src/components/backend/Datasources/CreateEditRelationship.svelte index f6621c1508..af678a88ba 100644 --- a/packages/builder/src/components/backend/Datasources/CreateEditRelationship.svelte +++ b/packages/builder/src/components/backend/Datasources/CreateEditRelationship.svelte @@ -33,6 +33,10 @@ part1: PrettyRelationshipDefinitions.MANY, part2: PrettyRelationshipDefinitions.ONE, }, + [RelationshipType.ONE_TO_MANY]: { + part1: PrettyRelationshipDefinitions.ONE, + part2: PrettyRelationshipDefinitions.MANY, + }, } let relationshipOpts1 = Object.values(PrettyRelationshipDefinitions) let relationshipOpts2 = Object.values(PrettyRelationshipDefinitions) @@ -58,7 +62,7 @@ let fromPrimary, fromForeign, fromColumn, toColumn let throughId, throughToKey, throughFromKey - let isManyToMany, isManyToOne, relationshipType + let relationshipType let hasValidated = false $: fromId = null @@ -85,8 +89,9 @@ $: valid = getErrorCount(errors) === 0 && allRequiredAttributesSet(relationshipType) $: isManyToMany = relationshipType === RelationshipType.MANY_TO_MANY - $: isManyToOne = relationshipType === RelationshipType.MANY_TO_ONE - + $: isManyToOne = + relationshipType === RelationshipType.MANY_TO_ONE || + relationshipType === RelationshipType.ONE_TO_MANY function getTable(id) { return plusTables.find(table => table._id === id) } diff --git a/packages/builder/src/components/backend/TableNavigator/TableNavigator.svelte b/packages/builder/src/components/backend/TableNavigator/TableNavigator.svelte index 056a36c4a7..712d74889c 100644 --- a/packages/builder/src/components/backend/TableNavigator/TableNavigator.svelte +++ b/packages/builder/src/components/backend/TableNavigator/TableNavigator.svelte @@ -53,7 +53,7 @@ selected={isViewActive(view, $isActive, $views, $viewsV2)} on:click={() => { if (view.version === 2) { - $goto(`./view/v2/${view.id}`) + $goto(`./view/v2/${encodeURIComponent(view.id)}`) } else { $goto(`./view/v1/${encodeURIComponent(name)}`) } diff --git a/packages/builder/src/components/design/settings/controls/EditComponentPopover.svelte b/packages/builder/src/components/design/settings/controls/EditComponentPopover.svelte index 26c1ced502..0ba502bbcb 100644 --- a/packages/builder/src/components/design/settings/controls/EditComponentPopover.svelte +++ b/packages/builder/src/components/design/settings/controls/EditComponentPopover.svelte @@ -11,6 +11,7 @@ export let componentBindings export let bindings export let parseSettings + export let disabled const draggable = getContext("draggable") const dispatch = createEventDispatcher() diff --git a/packages/builder/src/components/design/settings/controls/FieldConfiguration/CellEditor.svelte b/packages/builder/src/components/design/settings/controls/FieldConfiguration/CellEditor.svelte deleted file mode 100644 index e70decc035..0000000000 --- a/packages/builder/src/components/design/settings/controls/FieldConfiguration/CellEditor.svelte +++ /dev/null @@ -1,26 +0,0 @@ - - - - - - "{column.name}" field validation - - - -
- -
-
-
diff --git a/packages/builder/src/components/design/settings/controls/FieldConfiguration/ColumnDrawer.svelte b/packages/builder/src/components/design/settings/controls/FieldConfiguration/ColumnDrawer.svelte deleted file mode 100644 index 316bf56da3..0000000000 --- a/packages/builder/src/components/design/settings/controls/FieldConfiguration/ColumnDrawer.svelte +++ /dev/null @@ -1,202 +0,0 @@ - - - -
- - {#if columns?.length} - -
-
- - -
-
-
-
- {#each columns as column (column.id)} -
-
(dragDisabled = false)} - > - -
- - - removeColumn(column.id)} - disabled={columns.length === 1} - /> -
- {/each} -
- - {:else} -
-
- Add columns to be included in your form below. -
-
- {/if} -
-
- - - {#if columns?.length} - - {/if} -
-
- -
- - - diff --git a/packages/builder/src/components/design/settings/controls/FieldConfiguration/FieldConfiguration.svelte b/packages/builder/src/components/design/settings/controls/FieldConfiguration/FieldConfiguration.svelte index c1bf903584..8e2d8735d6 100644 --- a/packages/builder/src/components/design/settings/controls/FieldConfiguration/FieldConfiguration.svelte +++ b/packages/builder/src/components/design/settings/controls/FieldConfiguration/FieldConfiguration.svelte @@ -1,4 +1,5 @@
+
+ + { + let update = fieldList.map(field => ({ + ...field, + active: selectAll, + })) + listUpdated(update) + }} + text="" + bind:value={selectAll} + thin + /> +
{#if fieldList?.length} listUpdated(e.detail)} on:itemChange={processItemUpdate} items={fieldList} listItemKey={"_id"} @@ -172,4 +190,21 @@ .field-configuration :global(.spectrum-ActionButton) { width: 100%; } + .toggle-all { + display: flex; + justify-content: space-between; + } + .toggle-all :global(.spectrum-Switch) { + margin-right: 0px; + padding-right: calc(var(--spacing-s) - 1px); + min-height: unset; + } + .toggle-all :global(.spectrum-Switch .spectrum-Switch-switch) { + margin-top: 0px; + } + .toggle-all span { + color: var(--spectrum-global-color-gray-700); + font-size: 12px; + margin-left: calc(var(--spacing-s) - 1px); + } diff --git a/packages/cli/.eslintrc b/packages/cli/.eslintrc deleted file mode 100644 index 3431bf04fb..0000000000 --- a/packages/cli/.eslintrc +++ /dev/null @@ -1,12 +0,0 @@ -{ - "globals": { - "emit": true, - "key": true - }, - "env": { - "node": true - }, - "extends": ["eslint:recommended"], - "rules": { - } -} \ No newline at end of file diff --git a/packages/client/src/components/app/Text.svelte b/packages/client/src/components/app/Text.svelte index 6c16db25fd..1037725ff8 100644 --- a/packages/client/src/components/app/Text.svelte +++ b/packages/client/src/components/app/Text.svelte @@ -94,7 +94,7 @@ .align--right { text-align: right; } - .align-justify { + .align--justify { text-align: justify; } diff --git a/packages/client/src/components/app/forms/S3Upload.svelte b/packages/client/src/components/app/forms/S3Upload.svelte index 9985c83bb8..0147cbca6e 100644 --- a/packages/client/src/components/app/forms/S3Upload.svelte +++ b/packages/client/src/components/app/forms/S3Upload.svelte @@ -1,8 +1,7 @@