diff --git a/packages/builder/src/builderStore/store/screenTemplates/utils/commonComponents.js b/packages/builder/src/builderStore/store/screenTemplates/utils/commonComponents.js index 9176d535ab..ac40a10fea 100644 --- a/packages/builder/src/builderStore/store/screenTemplates/utils/commonComponents.js +++ b/packages/builder/src/builderStore/store/screenTemplates/utils/commonComponents.js @@ -138,6 +138,7 @@ const fieldTypeToComponentMap = { attachment: "attachmentfield", link: "relationshipfield", json: "jsonfield", + barcodeqr: "codescanner", } export function makeDatasourceFormComponents(datasource) { diff --git a/packages/builder/src/components/backend/DataTable/modals/CreateEditColumn.svelte b/packages/builder/src/components/backend/DataTable/modals/CreateEditColumn.svelte index 3fd38bddeb..b7249ad60c 100644 --- a/packages/builder/src/components/backend/DataTable/modals/CreateEditColumn.svelte +++ b/packages/builder/src/components/backend/DataTable/modals/CreateEditColumn.svelte @@ -261,6 +261,7 @@ } else { return [ FIELDS.STRING, + FIELDS.BARCODEQR, FIELDS.LONGFORM, FIELDS.OPTIONS, FIELDS.DATETIME, diff --git a/packages/builder/src/components/backend/TableNavigator/TableDataImport.svelte b/packages/builder/src/components/backend/TableNavigator/TableDataImport.svelte index 774aac0677..ea0ce59169 100644 --- a/packages/builder/src/components/backend/TableNavigator/TableDataImport.svelte +++ b/packages/builder/src/components/backend/TableNavigator/TableDataImport.svelte @@ -124,6 +124,14 @@ label: "Multi-select", value: FIELDS.ARRAY.type, }, + { + label: "Barcode/QR", + value: FIELDS.BARCODEQR.type, + }, + { + label: "Long Form Text", + value: FIELDS.LONGFORM.type, + }, ] diff --git a/packages/builder/src/components/design/settings/componentSettings.js b/packages/builder/src/components/design/settings/componentSettings.js index 56ae3de490..c3b81968f4 100644 --- a/packages/builder/src/components/design/settings/componentSettings.js +++ b/packages/builder/src/components/design/settings/componentSettings.js @@ -51,6 +51,7 @@ const componentMap = { "field/link": FormFieldSelect, "field/array": FormFieldSelect, "field/json": FormFieldSelect, + "field/barcode/qr": FormFieldSelect, // Some validation types are the same as others, so not all types are // explicitly listed here. e.g. options uses string validation "validation/string": ValidationEditor, diff --git a/packages/builder/src/components/design/settings/controls/FormFieldSelect.svelte b/packages/builder/src/components/design/settings/controls/FormFieldSelect.svelte index 1f08c56ff5..a02ea41099 100644 --- a/packages/builder/src/components/design/settings/controls/FormFieldSelect.svelte +++ b/packages/builder/src/components/design/settings/controls/FormFieldSelect.svelte @@ -24,18 +24,17 @@ const getOptions = (schema, type) => { let entries = Object.entries(schema ?? {}) - let types = [] - if (type === "field/options") { + if (type === "field/options" || type === "field/barcode/qr") { // allow options to be used on both options and string fields types = [type, "field/string"] } else { types = [type] } - types = types.map(type => type.split("/")[1]) - entries = entries.filter(entry => types.includes(entry[1].type)) + types = types.map(type => type.slice(type.indexOf("/") + 1)) + entries = entries.filter(entry => types.includes(entry[1].type)) return entries.map(entry => entry[0]) } diff --git a/packages/builder/src/constants/backend/index.js b/packages/builder/src/constants/backend/index.js index 28ce35d9f7..427e8d80a1 100644 --- a/packages/builder/src/constants/backend/index.js +++ b/packages/builder/src/constants/backend/index.js @@ -8,6 +8,15 @@ export const FIELDS = { presence: false, }, }, + BARCODEQR: { + name: "Barcode/QR", + type: "barcodeqr", + constraints: { + type: "string", + length: {}, + presence: false, + }, + }, LONGFORM: { name: "Long Form Text", type: "longform", @@ -148,6 +157,7 @@ export const ALLOWABLE_STRING_OPTIONS = [ FIELDS.STRING, FIELDS.OPTIONS, FIELDS.LONGFORM, + FIELDS.BARCODEQR, ] export const ALLOWABLE_STRING_TYPES = ALLOWABLE_STRING_OPTIONS.map( opt => opt.type diff --git a/packages/builder/src/pages/builder/app/[application]/design/[screenId]/components/[componentId]/new/_components/componentStructure.json b/packages/builder/src/pages/builder/app/[application]/design/[screenId]/components/[componentId]/new/_components/componentStructure.json index 088f0c0989..3d9cf7e2a3 100644 --- a/packages/builder/src/pages/builder/app/[application]/design/[screenId]/components/[componentId]/new/_components/componentStructure.json +++ b/packages/builder/src/pages/builder/app/[application]/design/[screenId]/components/[componentId]/new/_components/componentStructure.json @@ -66,7 +66,8 @@ "relationshipfield", "datetimefield", "multifieldselect", - "s3upload" + "s3upload", + "codescanner" ] }, { diff --git a/packages/client/manifest.json b/packages/client/manifest.json index bd5854dc9f..0edb5c0f39 100644 --- a/packages/client/manifest.json +++ b/packages/client/manifest.json @@ -3157,6 +3157,56 @@ } ] }, + "codescanner": { + "name": "Barcode/QR Scanner", + "icon": "Camera", + "styles": [ + "size" + ], + "illegalChildren": [ + "section" + ], + "settings": [ + { + "type": "field/barcode/qr", + "label": "Field", + "key": "field", + "required": true + }, + { + "type": "text", + "label": "Label", + "key": "label" + }, + { + "type": "text", + "label": "Button text", + "key": "scanButtonText" + }, + { + "type": "text", + "label": "Default value", + "key": "defaultValue" + }, + { + "type": "boolean", + "label": "Disabled", + "key": "disabled", + "defaultValue": false + }, + { + "type": "boolean", + "label": "Allow manual entry", + "key": "allowManualEntry", + "defaultValue": false + }, + { + "type": "validation/string", + "label": "Validation", + "key": "validation" + } + ] + }, "embeddedmap": { "name": "Embedded Map", "icon": "Location", @@ -4399,7 +4449,9 @@ "formblock": { "name": "Form Block", "icon": "Form", - "styles": ["size"], + "styles": [ + "size" + ], "block": true, "info": "Form blocks are only compatible with internal or SQL tables", "settings": [ @@ -4407,7 +4459,11 @@ "type": "select", "label": "Type", "key": "actionType", - "options": ["Create", "Update", "View"], + "options": [ + "Create", + "Update", + "View" + ], "defaultValue": "Create" }, { diff --git a/packages/client/package.json b/packages/client/package.json index 3040a731a7..dca199527a 100644 --- a/packages/client/package.json +++ b/packages/client/package.json @@ -33,6 +33,7 @@ "apexcharts": "^3.22.1", "dayjs": "^1.10.5", "downloadjs": "1.4.7", + "html5-qrcode": "^2.2.1", "leaflet": "^1.7.1", "regexparam": "^1.3.0", "sanitize-html": "^2.7.0", diff --git a/packages/client/rollup.config.js b/packages/client/rollup.config.js index 5206b63884..be94ace90c 100644 --- a/packages/client/rollup.config.js +++ b/packages/client/rollup.config.js @@ -27,6 +27,15 @@ export default { file: `./dist/budibase-client.js`, }, ], + onwarn(warning, warn) { + if ( + warning.code === "THIS_IS_UNDEFINED" || + warning.code === "CIRCULAR_DEPENDENCY" + ) { + return + } + warn(warning) + }, plugins: [ alias({ entries: [ diff --git a/packages/client/src/components/app/forms/CodeScanner.svelte b/packages/client/src/components/app/forms/CodeScanner.svelte new file mode 100644 index 0000000000..5dff3a96fa --- /dev/null +++ b/packages/client/src/components/app/forms/CodeScanner.svelte @@ -0,0 +1,234 @@ + + +
+ {#if value && !manualMode} +
+ + {value} +
+ {/if} + + {#if allowManualEntry && manualMode} +
+ { + dispatch("change", value) + }} + /> +
+ {/if} + + {#if value} + { + dispatch("change", "") + }} + {disabled} + > + Clear + + {:else} + { + showReaderModal() + }} + {disabled} + > + {scanButtonText} + + {/if} +
+ + + + diff --git a/packages/client/src/components/app/forms/CodeScannerField.svelte b/packages/client/src/components/app/forms/CodeScannerField.svelte new file mode 100644 index 0000000000..7e020aa9c7 --- /dev/null +++ b/packages/client/src/components/app/forms/CodeScannerField.svelte @@ -0,0 +1,47 @@ + + + + {#if fieldState} + + {/if} + diff --git a/packages/client/src/components/app/forms/index.js b/packages/client/src/components/app/forms/index.js index 0ff82cea94..44c1516885 100644 --- a/packages/client/src/components/app/forms/index.js +++ b/packages/client/src/components/app/forms/index.js @@ -13,3 +13,4 @@ export { default as passwordfield } from "./PasswordField.svelte" export { default as formstep } from "./FormStep.svelte" export { default as jsonfield } from "./JSONField.svelte" export { default as s3upload } from "./S3Upload.svelte" +export { default as codescanner } from "./CodeScannerField.svelte" diff --git a/packages/client/src/constants.js b/packages/client/src/constants.js index 51ef3fd124..1560552dc4 100644 --- a/packages/client/src/constants.js +++ b/packages/client/src/constants.js @@ -1,5 +1,6 @@ export const FieldTypes = { STRING: "string", + BARCODEQR: "barcodeqr", LONGFORM: "longform", OPTIONS: "options", NUMBER: "number", diff --git a/packages/server/src/constants/index.js b/packages/server/src/constants/index.js index c002c10f7b..2a92e87ff8 100644 --- a/packages/server/src/constants/index.js +++ b/packages/server/src/constants/index.js @@ -31,6 +31,7 @@ exports.NoEmptyFilterStrings = [ exports.FieldTypes = { STRING: "string", + BARCODEQR: "barcodeqr", LONGFORM: "longform", OPTIONS: "options", NUMBER: "number", @@ -51,6 +52,7 @@ exports.CanSwitchTypes = [ exports.FieldTypes.STRING, exports.FieldTypes.OPTIONS, exports.FieldTypes.LONGFORM, + exports.FieldTypes.BARCODEQR, ], [exports.FieldTypes.BOOLEAN, exports.FieldTypes.NUMBER], ] diff --git a/packages/server/src/integrations/base/sqlTable.ts b/packages/server/src/integrations/base/sqlTable.ts index af561e81c5..02f7ad8718 100644 --- a/packages/server/src/integrations/base/sqlTable.ts +++ b/packages/server/src/integrations/base/sqlTable.ts @@ -40,6 +40,7 @@ function generateSchema( case FieldTypes.STRING: case FieldTypes.OPTIONS: case FieldTypes.LONGFORM: + case FieldTypes.BARCODEQR: schema.text(key) break case FieldTypes.NUMBER: diff --git a/packages/server/src/utilities/csvParser.js b/packages/server/src/utilities/csvParser.js index 8f9b3373c9..09449f6fc1 100644 --- a/packages/server/src/utilities/csvParser.js +++ b/packages/server/src/utilities/csvParser.js @@ -4,6 +4,7 @@ const { FieldTypes } = require("../constants") const VALIDATORS = { [FieldTypes.STRING]: () => true, [FieldTypes.OPTIONS]: () => true, + [FieldTypes.BARCODEQR]: () => true, [FieldTypes.NUMBER]: attribute => { // allow not to be present if (!attribute) { diff --git a/packages/server/src/utilities/rowProcessor/index.js b/packages/server/src/utilities/rowProcessor/index.js index e4c364eaf3..91daa1b5a0 100644 --- a/packages/server/src/utilities/rowProcessor/index.js +++ b/packages/server/src/utilities/rowProcessor/index.js @@ -48,6 +48,11 @@ const TYPE_TRANSFORM_MAP = { [null]: "", [undefined]: undefined, }, + [FieldTypes.BARCODEQR]: { + "": "", + [null]: "", + [undefined]: undefined, + }, [FieldTypes.FORMULA]: { "": "", [null]: "", diff --git a/packages/types/src/sdk/datasources.ts b/packages/types/src/sdk/datasources.ts index c890e57881..279a76446f 100644 --- a/packages/types/src/sdk/datasources.ts +++ b/packages/types/src/sdk/datasources.ts @@ -24,6 +24,7 @@ export enum QueryType { export enum DatasourceFieldType { STRING = "string", + CODE = "code", LONGFORM = "longForm", BOOLEAN = "boolean", NUMBER = "number",