Merge pull request #13356 from Budibase/BUDI-8122/single-attachment-column-type

Single attachment column type
This commit is contained in:
Adria Navarro 2024-04-03 16:15:24 +02:00 committed by GitHub
commit d8afb0f58c
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
29 changed files with 200 additions and 73 deletions

View File

@ -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 (

View File

@ -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,
] ]

View File

@ -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,

View File

@ -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",

View File

@ -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",

View File

@ -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/*"],

View File

@ -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",

View File

@ -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,
} }

View File

@ -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}

View File

@ -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}
/>

View File

@ -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"

View File

@ -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
} }

View File

@ -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}

View File

@ -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}
/>

View File

@ -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,

View File

@ -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",

View File

@ -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 }
} }

View File

@ -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 },
}, },

View File

@ -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,

View File

@ -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)
} }
} }

View File

@ -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 {

View File

@ -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 =

View File

@ -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

View File

@ -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

View File

@ -34,7 +34,7 @@ function table(): Table {
schema: { schema: {
attach: { attach: {
name: "attach", name: "attach",
type: FieldType.ATTACHMENT, type: FieldType.ATTACHMENTS,
constraints: {}, constraints: {},
}, },
}, },

View File

@ -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: {},
}, },

View File

@ -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,

View File

@ -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,
},
} }

View File

@ -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
} }