Merge pull request #13356 from Budibase/BUDI-8122/single-attachment-column-type
Single attachment column type
This commit is contained in:
commit
d8afb0f58c
|
@ -67,7 +67,7 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
$: showDropzone =
|
$: showDropzone =
|
||||||
(!maximum || (maximum && value?.length < maximum)) && !disabled
|
(!maximum || (maximum && (value?.length || 0) < maximum)) && !disabled
|
||||||
|
|
||||||
async function processFileList(fileList) {
|
async function processFileList(fileList) {
|
||||||
if (
|
if (
|
||||||
|
|
|
@ -9,7 +9,7 @@ const MAX_DEPTH = 1
|
||||||
const TYPES_TO_SKIP = [
|
const TYPES_TO_SKIP = [
|
||||||
FieldType.FORMULA,
|
FieldType.FORMULA,
|
||||||
FieldType.LONGFORM,
|
FieldType.LONGFORM,
|
||||||
FieldType.ATTACHMENT,
|
FieldType.ATTACHMENTS,
|
||||||
//https://github.com/Budibase/budibase/issues/3030
|
//https://github.com/Budibase/budibase/issues/3030
|
||||||
FieldType.INTERNAL,
|
FieldType.INTERNAL,
|
||||||
]
|
]
|
||||||
|
|
|
@ -394,7 +394,8 @@
|
||||||
FIELDS.BIGINT,
|
FIELDS.BIGINT,
|
||||||
FIELDS.BOOLEAN,
|
FIELDS.BOOLEAN,
|
||||||
FIELDS.DATETIME,
|
FIELDS.DATETIME,
|
||||||
FIELDS.ATTACHMENT,
|
FIELDS.ATTACHMENT_SINGLE,
|
||||||
|
FIELDS.ATTACHMENTS,
|
||||||
FIELDS.LINK,
|
FIELDS.LINK,
|
||||||
FIELDS.FORMULA,
|
FIELDS.FORMULA,
|
||||||
FIELDS.JSON,
|
FIELDS.JSON,
|
||||||
|
|
|
@ -41,7 +41,8 @@ export const FieldTypeToComponentMap = {
|
||||||
[FieldType.BOOLEAN]: "booleanfield",
|
[FieldType.BOOLEAN]: "booleanfield",
|
||||||
[FieldType.LONGFORM]: "longformfield",
|
[FieldType.LONGFORM]: "longformfield",
|
||||||
[FieldType.DATETIME]: "datetimefield",
|
[FieldType.DATETIME]: "datetimefield",
|
||||||
[FieldType.ATTACHMENT]: "attachmentfield",
|
[FieldType.ATTACHMENTS]: "attachmentfield",
|
||||||
|
[FieldType.ATTACHMENT_SINGLE]: "attachmentsinglefield",
|
||||||
[FieldType.LINK]: "relationshipfield",
|
[FieldType.LINK]: "relationshipfield",
|
||||||
[FieldType.JSON]: "jsonfield",
|
[FieldType.JSON]: "jsonfield",
|
||||||
[FieldType.BARCODEQR]: "codescanner",
|
[FieldType.BARCODEQR]: "codescanner",
|
||||||
|
|
|
@ -107,10 +107,18 @@ export const FIELDS = {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
ATTACHMENT: {
|
ATTACHMENT_SINGLE: {
|
||||||
name: "Attachment",
|
name: "Attachment",
|
||||||
type: FieldType.ATTACHMENT,
|
type: FieldType.ATTACHMENT_SINGLE,
|
||||||
icon: "Folder",
|
icon: "Document",
|
||||||
|
constraints: {
|
||||||
|
presence: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
ATTACHMENTS: {
|
||||||
|
name: "Attachment List",
|
||||||
|
type: FieldType.ATTACHMENTS,
|
||||||
|
icon: "AppleFiles",
|
||||||
constraints: {
|
constraints: {
|
||||||
type: "array",
|
type: "array",
|
||||||
presence: false,
|
presence: false,
|
||||||
|
@ -299,7 +307,7 @@ export const PaginationLocations = [
|
||||||
|
|
||||||
export const BannedSearchTypes = [
|
export const BannedSearchTypes = [
|
||||||
FieldType.LINK,
|
FieldType.LINK,
|
||||||
FieldType.ATTACHMENT,
|
FieldType.ATTACHMENTS,
|
||||||
FieldType.FORMULA,
|
FieldType.FORMULA,
|
||||||
FieldType.JSON,
|
FieldType.JSON,
|
||||||
"jsonarray",
|
"jsonarray",
|
||||||
|
|
|
@ -1,9 +1,6 @@
|
||||||
{
|
{
|
||||||
"extends": "./tsconfig.build.json",
|
"extends": "./tsconfig.build.json",
|
||||||
"compilerOptions": {
|
"compilerOptions": {
|
||||||
"composite": true,
|
|
||||||
"declaration": true,
|
|
||||||
"sourceMap": true,
|
|
||||||
"baseUrl": ".",
|
"baseUrl": ".",
|
||||||
"paths": {
|
"paths": {
|
||||||
"assets/*": ["./assets/*"],
|
"assets/*": ["./assets/*"],
|
||||||
|
|
|
@ -4226,7 +4226,7 @@
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"attachmentfield": {
|
"attachmentfield": {
|
||||||
"name": "Attachment",
|
"name": "Attachment list",
|
||||||
"icon": "Attach",
|
"icon": "Attach",
|
||||||
"styles": ["size"],
|
"styles": ["size"],
|
||||||
"requiredAncestors": ["form"],
|
"requiredAncestors": ["form"],
|
||||||
|
@ -4322,6 +4322,103 @@
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
"attachmentsinglefield": {
|
||||||
|
"name": "Single Attachment",
|
||||||
|
"icon": "Attach",
|
||||||
|
"styles": ["size"],
|
||||||
|
"requiredAncestors": ["form"],
|
||||||
|
"editable": true,
|
||||||
|
"size": {
|
||||||
|
"width": 400,
|
||||||
|
"height": 200
|
||||||
|
},
|
||||||
|
"settings": [
|
||||||
|
{
|
||||||
|
"type": "field/attachment_single",
|
||||||
|
"label": "Field",
|
||||||
|
"key": "field",
|
||||||
|
"required": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "text",
|
||||||
|
"label": "Label",
|
||||||
|
"key": "label"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "text",
|
||||||
|
"label": "Help text",
|
||||||
|
"key": "helpText"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "text",
|
||||||
|
"label": "Extensions",
|
||||||
|
"key": "extensions"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "number",
|
||||||
|
"label": "Max attachments",
|
||||||
|
"key": "maximum",
|
||||||
|
"min": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "event",
|
||||||
|
"label": "On change",
|
||||||
|
"key": "onChange",
|
||||||
|
"context": [
|
||||||
|
{
|
||||||
|
"label": "Field Value",
|
||||||
|
"key": "value"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "boolean",
|
||||||
|
"label": "Compact",
|
||||||
|
"key": "compact",
|
||||||
|
"defaultValue": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "boolean",
|
||||||
|
"label": "Read only",
|
||||||
|
"key": "disabled",
|
||||||
|
"defaultValue": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "validation/attachment",
|
||||||
|
"label": "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"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
"relationshipfield": {
|
"relationshipfield": {
|
||||||
"name": "Relationship Picker",
|
"name": "Relationship Picker",
|
||||||
"icon": "TaskList",
|
"icon": "TaskList",
|
||||||
|
|
|
@ -15,7 +15,8 @@
|
||||||
[FieldType.BOOLEAN]: "booleanfield",
|
[FieldType.BOOLEAN]: "booleanfield",
|
||||||
[FieldType.LONGFORM]: "longformfield",
|
[FieldType.LONGFORM]: "longformfield",
|
||||||
[FieldType.DATETIME]: "datetimefield",
|
[FieldType.DATETIME]: "datetimefield",
|
||||||
[FieldType.ATTACHMENT]: "attachmentfield",
|
[FieldType.ATTACHMENTS]: "attachmentfield",
|
||||||
|
[FieldType.ATTACHMENT_SINGLE]: "attachmentsinglefield",
|
||||||
[FieldType.LINK]: "relationshipfield",
|
[FieldType.LINK]: "relationshipfield",
|
||||||
[FieldType.JSON]: "jsonfield",
|
[FieldType.JSON]: "jsonfield",
|
||||||
[FieldType.BARCODEQR]: "codescanner",
|
[FieldType.BARCODEQR]: "codescanner",
|
||||||
|
@ -60,7 +61,7 @@
|
||||||
|
|
||||||
function getPropsByType(field) {
|
function getPropsByType(field) {
|
||||||
const propsMapByType = {
|
const propsMapByType = {
|
||||||
[FieldType.ATTACHMENT]: (_field, schema) => {
|
[FieldType.ATTACHMENTS]: (_field, schema) => {
|
||||||
return {
|
return {
|
||||||
maximum: schema?.constraints?.length?.maximum,
|
maximum: schema?.constraints?.length?.maximum,
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
<script>
|
<script>
|
||||||
import Field from "./Field.svelte"
|
import Field from "./Field.svelte"
|
||||||
import { CoreDropzone } from "@budibase/bbui"
|
import { CoreDropzone } from "@budibase/bbui"
|
||||||
|
import { FieldType } from "@budibase/types"
|
||||||
import { getContext } from "svelte"
|
import { getContext } from "svelte"
|
||||||
|
|
||||||
export let field
|
export let field
|
||||||
|
@ -14,6 +15,11 @@
|
||||||
export let maximum = undefined
|
export let maximum = undefined
|
||||||
export let span
|
export let span
|
||||||
export let helpText = null
|
export let helpText = null
|
||||||
|
export let type = FieldType.ATTACHMENTS
|
||||||
|
export let fieldApiMapper = {
|
||||||
|
get: value => value,
|
||||||
|
set: value => value,
|
||||||
|
}
|
||||||
|
|
||||||
let fieldState
|
let fieldState
|
||||||
let fieldApi
|
let fieldApi
|
||||||
|
@ -63,9 +69,10 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleChange = e => {
|
const handleChange = e => {
|
||||||
const changed = fieldApi.setValue(e.detail)
|
const value = fieldApiMapper.set(e.detail)
|
||||||
|
const changed = fieldApi.setValue(value)
|
||||||
if (onChange && changed) {
|
if (onChange && changed) {
|
||||||
onChange({ value: e.detail })
|
onChange({ value })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
@ -78,14 +85,14 @@
|
||||||
{validation}
|
{validation}
|
||||||
{span}
|
{span}
|
||||||
{helpText}
|
{helpText}
|
||||||
type="attachment"
|
{type}
|
||||||
bind:fieldState
|
bind:fieldState
|
||||||
bind:fieldApi
|
bind:fieldApi
|
||||||
defaultValue={[]}
|
defaultValue={[]}
|
||||||
>
|
>
|
||||||
{#if fieldState}
|
{#if fieldState}
|
||||||
<CoreDropzone
|
<CoreDropzone
|
||||||
value={fieldState.value}
|
value={fieldApiMapper.get(fieldState.value)}
|
||||||
disabled={fieldState.disabled || fieldState.readonly}
|
disabled={fieldState.disabled || fieldState.readonly}
|
||||||
error={fieldState.error}
|
error={fieldState.error}
|
||||||
on:change={handleChange}
|
on:change={handleChange}
|
||||||
|
|
|
@ -0,0 +1,16 @@
|
||||||
|
<script>
|
||||||
|
import { FieldType } from "@budibase/types"
|
||||||
|
import AttachmentField from "./AttachmentField.svelte"
|
||||||
|
|
||||||
|
const fieldApiMapper = {
|
||||||
|
get: value => (!Array.isArray(value) && value ? [value] : value) || [],
|
||||||
|
set: value => value[0] || null,
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<AttachmentField
|
||||||
|
{...$$restProps}
|
||||||
|
type={FieldType.ATTACHMENT_SINGLE}
|
||||||
|
maximum={1}
|
||||||
|
{fieldApiMapper}
|
||||||
|
/>
|
|
@ -9,6 +9,7 @@ export { default as booleanfield } from "./BooleanField.svelte"
|
||||||
export { default as longformfield } from "./LongFormField.svelte"
|
export { default as longformfield } from "./LongFormField.svelte"
|
||||||
export { default as datetimefield } from "./DateTimeField.svelte"
|
export { default as datetimefield } from "./DateTimeField.svelte"
|
||||||
export { default as attachmentfield } from "./AttachmentField.svelte"
|
export { default as attachmentfield } from "./AttachmentField.svelte"
|
||||||
|
export { default as attachmentsinglefield } from "./AttachmentSingleField.svelte"
|
||||||
export { default as relationshipfield } from "./RelationshipField.svelte"
|
export { default as relationshipfield } from "./RelationshipField.svelte"
|
||||||
export { default as passwordfield } from "./PasswordField.svelte"
|
export { default as passwordfield } from "./PasswordField.svelte"
|
||||||
export { default as formstep } from "./FormStep.svelte"
|
export { default as formstep } from "./FormStep.svelte"
|
||||||
|
|
|
@ -192,7 +192,7 @@ const parseType = (value, type) => {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Parse attachments, treating no elements as null
|
// Parse attachments, treating no elements as null
|
||||||
if (type === FieldTypes.ATTACHMENT) {
|
if (type === FieldTypes.ATTACHMENTS) {
|
||||||
if (!Array.isArray(value) || !value.length) {
|
if (!Array.isArray(value) || !value.length) {
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,6 +10,7 @@
|
||||||
export let invertX = false
|
export let invertX = false
|
||||||
export let invertY = false
|
export let invertY = false
|
||||||
export let schema
|
export let schema
|
||||||
|
export let maximum
|
||||||
|
|
||||||
const { API, notifications } = getContext("grid")
|
const { API, notifications } = getContext("grid")
|
||||||
const imageExtensions = ["png", "tiff", "gif", "raw", "jpg", "jpeg"]
|
const imageExtensions = ["png", "tiff", "gif", "raw", "jpg", "jpeg"]
|
||||||
|
@ -98,7 +99,7 @@
|
||||||
{value}
|
{value}
|
||||||
compact
|
compact
|
||||||
on:change={e => onChange(e.detail)}
|
on:change={e => onChange(e.detail)}
|
||||||
maximum={schema.constraints?.length?.maximum}
|
maximum={maximum || schema.constraints?.length?.maximum}
|
||||||
{processFiles}
|
{processFiles}
|
||||||
{deleteAttachments}
|
{deleteAttachments}
|
||||||
{handleFileTooLarge}
|
{handleFileTooLarge}
|
||||||
|
|
|
@ -0,0 +1,20 @@
|
||||||
|
<script>
|
||||||
|
import AttachmentCell from "./AttachmentCell.svelte"
|
||||||
|
|
||||||
|
export let value
|
||||||
|
export let onChange
|
||||||
|
|
||||||
|
$: arrayValue = (!Array.isArray(value) && value ? [value] : value) || []
|
||||||
|
|
||||||
|
$: onFileChange = value => {
|
||||||
|
value = value[0] || null
|
||||||
|
onChange(value)
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<AttachmentCell
|
||||||
|
{...$$restProps}
|
||||||
|
maximum={1}
|
||||||
|
value={arrayValue}
|
||||||
|
onChange={onFileChange}
|
||||||
|
/>
|
|
@ -11,6 +11,7 @@ import BooleanCell from "../cells/BooleanCell.svelte"
|
||||||
import FormulaCell from "../cells/FormulaCell.svelte"
|
import FormulaCell from "../cells/FormulaCell.svelte"
|
||||||
import JSONCell from "../cells/JSONCell.svelte"
|
import JSONCell from "../cells/JSONCell.svelte"
|
||||||
import AttachmentCell from "../cells/AttachmentCell.svelte"
|
import AttachmentCell from "../cells/AttachmentCell.svelte"
|
||||||
|
import AttachmentSingleCell from "../cells/AttachmentSingleCell.svelte"
|
||||||
import BBReferenceCell from "../cells/BBReferenceCell.svelte"
|
import BBReferenceCell from "../cells/BBReferenceCell.svelte"
|
||||||
|
|
||||||
const TypeComponentMap = {
|
const TypeComponentMap = {
|
||||||
|
@ -22,7 +23,8 @@ const TypeComponentMap = {
|
||||||
[FieldType.ARRAY]: MultiSelectCell,
|
[FieldType.ARRAY]: MultiSelectCell,
|
||||||
[FieldType.NUMBER]: NumberCell,
|
[FieldType.NUMBER]: NumberCell,
|
||||||
[FieldType.BOOLEAN]: BooleanCell,
|
[FieldType.BOOLEAN]: BooleanCell,
|
||||||
[FieldType.ATTACHMENT]: AttachmentCell,
|
[FieldType.ATTACHMENTS]: AttachmentCell,
|
||||||
|
[FieldType.ATTACHMENT_SINGLE]: AttachmentSingleCell,
|
||||||
[FieldType.LINK]: RelationshipCell,
|
[FieldType.LINK]: RelationshipCell,
|
||||||
[FieldType.FORMULA]: FormulaCell,
|
[FieldType.FORMULA]: FormulaCell,
|
||||||
[FieldType.JSON]: JSONCell,
|
[FieldType.JSON]: JSONCell,
|
||||||
|
|
|
@ -16,7 +16,8 @@ const TypeIconMap = {
|
||||||
[FieldType.ARRAY]: "Dropdown",
|
[FieldType.ARRAY]: "Dropdown",
|
||||||
[FieldType.NUMBER]: "123",
|
[FieldType.NUMBER]: "123",
|
||||||
[FieldType.BOOLEAN]: "Boolean",
|
[FieldType.BOOLEAN]: "Boolean",
|
||||||
[FieldType.ATTACHMENT]: "AppleFiles",
|
[FieldType.ATTACHMENTS]: "AppleFiles",
|
||||||
|
[FieldType.ATTACHMENT_SINGLE]: "Document",
|
||||||
[FieldType.LINK]: "DataCorrelated",
|
[FieldType.LINK]: "DataCorrelated",
|
||||||
[FieldType.FORMULA]: "Calculator",
|
[FieldType.FORMULA]: "Calculator",
|
||||||
[FieldType.JSON]: "Brackets",
|
[FieldType.JSON]: "Brackets",
|
||||||
|
|
|
@ -30,8 +30,6 @@ import {
|
||||||
View,
|
View,
|
||||||
RelationshipFieldMetadata,
|
RelationshipFieldMetadata,
|
||||||
FieldType,
|
FieldType,
|
||||||
FieldTypeSubtypes,
|
|
||||||
AttachmentFieldMetadata,
|
|
||||||
} from "@budibase/types"
|
} from "@budibase/types"
|
||||||
|
|
||||||
export async function clearColumns(table: Table, columnNames: string[]) {
|
export async function clearColumns(table: Table, columnNames: string[]) {
|
||||||
|
@ -91,26 +89,6 @@ export async function checkForColumnUpdates(
|
||||||
await checkForViewUpdates(updatedTable, deletedColumns, columnRename)
|
await checkForViewUpdates(updatedTable, deletedColumns, columnRename)
|
||||||
}
|
}
|
||||||
|
|
||||||
const changedAttachmentSubtypeColumns = Object.values(
|
|
||||||
updatedTable.schema
|
|
||||||
).filter(
|
|
||||||
(column): column is AttachmentFieldMetadata =>
|
|
||||||
column.type === FieldType.ATTACHMENT &&
|
|
||||||
column.subtype !== oldTable?.schema[column.name]?.subtype
|
|
||||||
)
|
|
||||||
for (const attachmentColumn of changedAttachmentSubtypeColumns) {
|
|
||||||
if (attachmentColumn.subtype === FieldTypeSubtypes.ATTACHMENT.SINGLE) {
|
|
||||||
attachmentColumn.constraints ??= { length: {} }
|
|
||||||
attachmentColumn.constraints.length ??= {}
|
|
||||||
attachmentColumn.constraints.length.maximum = 1
|
|
||||||
attachmentColumn.constraints.length.message =
|
|
||||||
"cannot contain multiple files"
|
|
||||||
} else {
|
|
||||||
delete attachmentColumn.constraints?.length?.maximum
|
|
||||||
delete attachmentColumn.constraints?.length?.message
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return { rows: updatedRows, table: updatedTable }
|
return { rows: updatedRows, table: updatedTable }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -234,7 +234,7 @@ describe.each([
|
||||||
constraints: { type: "string", presence: false },
|
constraints: { type: "string", presence: false },
|
||||||
}
|
}
|
||||||
const attachment: FieldSchema = {
|
const attachment: FieldSchema = {
|
||||||
type: FieldType.ATTACHMENT,
|
type: FieldType.ATTACHMENTS,
|
||||||
name: "attachment",
|
name: "attachment",
|
||||||
constraints: { type: "array", presence: false },
|
constraints: { type: "array", presence: false },
|
||||||
}
|
}
|
||||||
|
@ -790,7 +790,7 @@ describe.each([
|
||||||
defaultTable({
|
defaultTable({
|
||||||
schema: {
|
schema: {
|
||||||
attachment: {
|
attachment: {
|
||||||
type: FieldType.ATTACHMENT,
|
type: FieldType.ATTACHMENTS,
|
||||||
name: "attachment",
|
name: "attachment",
|
||||||
constraints: { type: "array", presence: false },
|
constraints: { type: "array", presence: false },
|
||||||
},
|
},
|
||||||
|
|
|
@ -299,7 +299,7 @@ export const DEFAULT_EMPLOYEE_TABLE_SCHEMA: Table = {
|
||||||
sortable: false,
|
sortable: false,
|
||||||
},
|
},
|
||||||
"Badge Photo": {
|
"Badge Photo": {
|
||||||
type: FieldType.ATTACHMENT,
|
type: FieldType.ATTACHMENTS,
|
||||||
constraints: {
|
constraints: {
|
||||||
type: FieldType.ARRAY,
|
type: FieldType.ARRAY,
|
||||||
presence: false,
|
presence: false,
|
||||||
|
@ -607,7 +607,7 @@ export const DEFAULT_EXPENSES_TABLE_SCHEMA: Table = {
|
||||||
ignoreTimezones: true,
|
ignoreTimezones: true,
|
||||||
},
|
},
|
||||||
Attachment: {
|
Attachment: {
|
||||||
type: FieldType.ATTACHMENT,
|
type: FieldType.ATTACHMENTS,
|
||||||
constraints: {
|
constraints: {
|
||||||
type: FieldType.ARRAY,
|
type: FieldType.ARRAY,
|
||||||
presence: false,
|
presence: false,
|
||||||
|
|
|
@ -30,7 +30,7 @@ export async function getRowsWithAttachments(appId: string, table: Table) {
|
||||||
const db = dbCore.getDB(appId)
|
const db = dbCore.getDB(appId)
|
||||||
const attachmentCols: string[] = []
|
const attachmentCols: string[] = []
|
||||||
for (let [key, column] of Object.entries(table.schema)) {
|
for (let [key, column] of Object.entries(table.schema)) {
|
||||||
if (column.type === FieldType.ATTACHMENT) {
|
if (column.type === FieldType.ATTACHMENTS) {
|
||||||
attachmentCols.push(key)
|
attachmentCols.push(key)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -175,13 +175,13 @@ export async function validate({
|
||||||
errors[fieldName] = [`${fieldName} is required`]
|
errors[fieldName] = [`${fieldName} is required`]
|
||||||
}
|
}
|
||||||
} else if (
|
} else if (
|
||||||
(type === FieldType.ATTACHMENT || type === FieldType.JSON) &&
|
(type === FieldType.ATTACHMENTS || type === FieldType.JSON) &&
|
||||||
typeof row[fieldName] === "string"
|
typeof row[fieldName] === "string"
|
||||||
) {
|
) {
|
||||||
// this should only happen if there is an error
|
// this should only happen if there is an error
|
||||||
try {
|
try {
|
||||||
const json = JSON.parse(row[fieldName])
|
const json = JSON.parse(row[fieldName])
|
||||||
if (type === FieldType.ATTACHMENT) {
|
if (type === FieldType.ATTACHMENTS) {
|
||||||
if (Array.isArray(json)) {
|
if (Array.isArray(json)) {
|
||||||
row[fieldName] = json
|
row[fieldName] = json
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -34,7 +34,7 @@ export class AttachmentCleanup {
|
||||||
let files: string[] = []
|
let files: string[] = []
|
||||||
const tableSchema = opts.oldTable?.schema || table.schema
|
const tableSchema = opts.oldTable?.schema || table.schema
|
||||||
for (let [key, schema] of Object.entries(tableSchema)) {
|
for (let [key, schema] of Object.entries(tableSchema)) {
|
||||||
if (schema.type !== FieldType.ATTACHMENT) {
|
if (schema.type !== FieldType.ATTACHMENTS) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
const columnRemoved = opts.oldTable && !table.schema[key]
|
const columnRemoved = opts.oldTable && !table.schema[key]
|
||||||
|
@ -68,7 +68,7 @@ export class AttachmentCleanup {
|
||||||
return AttachmentCleanup.coreCleanup(() => {
|
return AttachmentCleanup.coreCleanup(() => {
|
||||||
let files: string[] = []
|
let files: string[] = []
|
||||||
for (let [key, schema] of Object.entries(table.schema)) {
|
for (let [key, schema] of Object.entries(table.schema)) {
|
||||||
if (schema.type !== FieldType.ATTACHMENT) {
|
if (schema.type !== FieldType.ATTACHMENTS) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
rows.forEach(row => {
|
rows.forEach(row => {
|
||||||
|
@ -88,7 +88,7 @@ export class AttachmentCleanup {
|
||||||
return AttachmentCleanup.coreCleanup(() => {
|
return AttachmentCleanup.coreCleanup(() => {
|
||||||
let files: string[] = []
|
let files: string[] = []
|
||||||
for (let [key, schema] of Object.entries(table.schema)) {
|
for (let [key, schema] of Object.entries(table.schema)) {
|
||||||
if (schema.type !== FieldType.ATTACHMENT) {
|
if (schema.type !== FieldType.ATTACHMENTS) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
const oldKeys =
|
const oldKeys =
|
||||||
|
|
|
@ -148,7 +148,7 @@ export async function inputProcessing(
|
||||||
}
|
}
|
||||||
|
|
||||||
// remove any attachment urls, they are generated on read
|
// remove any attachment urls, they are generated on read
|
||||||
if (field.type === FieldType.ATTACHMENT) {
|
if (field.type === FieldType.ATTACHMENTS) {
|
||||||
const attachments = clonedRow[key]
|
const attachments = clonedRow[key]
|
||||||
if (attachments?.length) {
|
if (attachments?.length) {
|
||||||
attachments.forEach((attachment: RowAttachment) => {
|
attachments.forEach((attachment: RowAttachment) => {
|
||||||
|
@ -216,7 +216,7 @@ export async function outputProcessing<T extends Row[] | Row>(
|
||||||
|
|
||||||
// process complex types: attachements, bb references...
|
// process complex types: attachements, bb references...
|
||||||
for (let [property, column] of Object.entries(table.schema)) {
|
for (let [property, column] of Object.entries(table.schema)) {
|
||||||
if (column.type === FieldType.ATTACHMENT) {
|
if (column.type === FieldType.ATTACHMENTS) {
|
||||||
for (let row of enriched) {
|
for (let row of enriched) {
|
||||||
if (row[property] == null || !Array.isArray(row[property])) {
|
if (row[property] == null || !Array.isArray(row[property])) {
|
||||||
continue
|
continue
|
||||||
|
|
|
@ -106,7 +106,7 @@ export const TYPE_TRANSFORM_MAP: any = {
|
||||||
return date
|
return date
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
[FieldType.ATTACHMENT]: {
|
[FieldType.ATTACHMENTS]: {
|
||||||
//@ts-ignore
|
//@ts-ignore
|
||||||
[null]: [],
|
[null]: [],
|
||||||
//@ts-ignore
|
//@ts-ignore
|
||||||
|
|
|
@ -34,7 +34,7 @@ function table(): Table {
|
||||||
schema: {
|
schema: {
|
||||||
attach: {
|
attach: {
|
||||||
name: "attach",
|
name: "attach",
|
||||||
type: FieldType.ATTACHMENT,
|
type: FieldType.ATTACHMENTS,
|
||||||
constraints: {},
|
constraints: {},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
|
@ -82,7 +82,7 @@ describe("rowProcessor - outputProcessing", () => {
|
||||||
sourceType: TableSourceType.INTERNAL,
|
sourceType: TableSourceType.INTERNAL,
|
||||||
schema: {
|
schema: {
|
||||||
attach: {
|
attach: {
|
||||||
type: FieldType.ATTACHMENT,
|
type: FieldType.ATTACHMENTS,
|
||||||
name: "attach",
|
name: "attach",
|
||||||
constraints: {},
|
constraints: {},
|
||||||
},
|
},
|
||||||
|
|
|
@ -11,10 +11,10 @@ const allowDisplayColumnByType: Record<FieldType, boolean> = {
|
||||||
[FieldType.INTERNAL]: true,
|
[FieldType.INTERNAL]: true,
|
||||||
[FieldType.BARCODEQR]: true,
|
[FieldType.BARCODEQR]: true,
|
||||||
[FieldType.BIGINT]: true,
|
[FieldType.BIGINT]: true,
|
||||||
|
|
||||||
[FieldType.BOOLEAN]: false,
|
[FieldType.BOOLEAN]: false,
|
||||||
[FieldType.ARRAY]: false,
|
[FieldType.ARRAY]: false,
|
||||||
[FieldType.ATTACHMENT]: false,
|
[FieldType.ATTACHMENTS]: false,
|
||||||
|
[FieldType.ATTACHMENT_SINGLE]: false,
|
||||||
[FieldType.LINK]: false,
|
[FieldType.LINK]: false,
|
||||||
[FieldType.JSON]: false,
|
[FieldType.JSON]: false,
|
||||||
[FieldType.BB_REFERENCE]: false,
|
[FieldType.BB_REFERENCE]: false,
|
||||||
|
@ -34,7 +34,8 @@ const allowSortColumnByType: Record<FieldType, boolean> = {
|
||||||
[FieldType.JSON]: true,
|
[FieldType.JSON]: true,
|
||||||
|
|
||||||
[FieldType.FORMULA]: false,
|
[FieldType.FORMULA]: false,
|
||||||
[FieldType.ATTACHMENT]: false,
|
[FieldType.ATTACHMENTS]: false,
|
||||||
|
[FieldType.ATTACHMENT_SINGLE]: false,
|
||||||
[FieldType.ARRAY]: false,
|
[FieldType.ARRAY]: false,
|
||||||
[FieldType.LINK]: false,
|
[FieldType.LINK]: false,
|
||||||
[FieldType.BB_REFERENCE]: false,
|
[FieldType.BB_REFERENCE]: false,
|
||||||
|
|
|
@ -8,7 +8,8 @@ export enum FieldType {
|
||||||
BOOLEAN = "boolean",
|
BOOLEAN = "boolean",
|
||||||
ARRAY = "array",
|
ARRAY = "array",
|
||||||
DATETIME = "datetime",
|
DATETIME = "datetime",
|
||||||
ATTACHMENT = "attachment",
|
ATTACHMENTS = "attachment",
|
||||||
|
ATTACHMENT_SINGLE = "attachment_single",
|
||||||
LINK = "link",
|
LINK = "link",
|
||||||
FORMULA = "formula",
|
FORMULA = "formula",
|
||||||
AUTO = "auto",
|
AUTO = "auto",
|
||||||
|
@ -38,7 +39,6 @@ export interface Row extends Document {
|
||||||
export enum FieldSubtype {
|
export enum FieldSubtype {
|
||||||
USER = "user",
|
USER = "user",
|
||||||
USERS = "users",
|
USERS = "users",
|
||||||
SINGLE = "single",
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// The 'as' are required for typescript not to type the outputs as generic FieldSubtype
|
// The 'as' are required for typescript not to type the outputs as generic FieldSubtype
|
||||||
|
@ -47,7 +47,4 @@ export const FieldTypeSubtypes = {
|
||||||
USER: FieldSubtype.USER as FieldSubtype.USER,
|
USER: FieldSubtype.USER as FieldSubtype.USER,
|
||||||
USERS: FieldSubtype.USERS as FieldSubtype.USERS,
|
USERS: FieldSubtype.USERS as FieldSubtype.USERS,
|
||||||
},
|
},
|
||||||
ATTACHMENT: {
|
|
||||||
SINGLE: FieldSubtype.SINGLE as FieldSubtype.SINGLE,
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -112,10 +112,8 @@ export interface BBReferenceFieldMetadata
|
||||||
relationshipType?: RelationshipType
|
relationshipType?: RelationshipType
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface AttachmentFieldMetadata
|
export interface AttachmentFieldMetadata extends BaseFieldSchema {
|
||||||
extends Omit<BaseFieldSchema, "subtype"> {
|
type: FieldType.ATTACHMENTS
|
||||||
type: FieldType.ATTACHMENT
|
|
||||||
subtype?: FieldSubtype.SINGLE
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface FieldConstraints {
|
export interface FieldConstraints {
|
||||||
|
@ -164,7 +162,7 @@ interface OtherFieldMetadata extends BaseFieldSchema {
|
||||||
| FieldType.NUMBER
|
| FieldType.NUMBER
|
||||||
| FieldType.LONGFORM
|
| FieldType.LONGFORM
|
||||||
| FieldType.BB_REFERENCE
|
| FieldType.BB_REFERENCE
|
||||||
| FieldType.ATTACHMENT
|
| FieldType.ATTACHMENTS
|
||||||
>
|
>
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -217,5 +215,5 @@ export function isBBReferenceField(
|
||||||
export function isAttachmentField(
|
export function isAttachmentField(
|
||||||
field: FieldSchema
|
field: FieldSchema
|
||||||
): field is AttachmentFieldMetadata {
|
): field is AttachmentFieldMetadata {
|
||||||
return field.type === FieldType.ATTACHMENT
|
return field.type === FieldType.ATTACHMENTS
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue