Files v2 - Add new FILES field type (#17236)
In this PR : - New field type FieldMetadataType.FILES added (as jsonb in DB) - Backend: GraphQL types, validation, REST API schema, data processor handling - Frontend: field configuration in settings - Feature flag IS_FILES_FIELD_ENABLED to gate the feature - Integration tests for create/filter validation - Dev seed: Feature flag enabled + FILES field on survey result object To do in next PRs : - Backend : -- new workspaceFile controller (for download) & new workspaceFile upload resolver (for upload) -- pre-hook/post-hook to ensure fileId existence + listener to ensure cleaning - Frontend : FILES field display/edit in table view, record, ...
This commit is contained in:
+30
-1
@@ -3,6 +3,7 @@ import {
|
||||
type FieldBooleanValue,
|
||||
type FieldDateTimeValue,
|
||||
type FieldDateValue,
|
||||
type FieldFilesValue,
|
||||
type FieldJsonValue,
|
||||
type FieldMultiSelectValue,
|
||||
type FieldNumberValue,
|
||||
@@ -14,11 +15,12 @@ import {
|
||||
import { DEFAULT_DATE_VALUE } from '@/settings/data-model/constants/DefaultDateValue';
|
||||
import { type SettingsFieldTypeCategoryType } from '@/settings/data-model/types/SettingsFieldTypeCategoryType';
|
||||
import { type SettingsNonCompositeFieldType } from '@/settings/data-model/types/SettingsNonCompositeFieldType';
|
||||
import { type FieldRatingValue } from 'twenty-shared/types';
|
||||
import { FILE_CATEGORIES, type FieldRatingValue } from 'twenty-shared/types';
|
||||
import {
|
||||
IllustrationIconArray,
|
||||
IllustrationIconCalendarEvent,
|
||||
IllustrationIconCalendarTime,
|
||||
IllustrationIconFile,
|
||||
IllustrationIconJson,
|
||||
IllustrationIconNumbers,
|
||||
IllustrationIconOneToMany,
|
||||
@@ -139,4 +141,31 @@ export const SETTINGS_NON_COMPOSITE_FIELD_TYPE_CONFIGS: SettingsNonCompositeFiel
|
||||
category: 'Advanced',
|
||||
exampleValues: [['value1', 'value2'], ['value3'], []],
|
||||
} as const satisfies SettingsFieldTypeConfig<FieldArrayValue>,
|
||||
[FieldMetadataType.FILES]: {
|
||||
label: 'Files',
|
||||
Icon: IllustrationIconFile,
|
||||
category: 'Advanced',
|
||||
exampleValues: [
|
||||
[
|
||||
{
|
||||
fileId: 'file-1',
|
||||
label: 'Document.pdf',
|
||||
fileCategory: FILE_CATEGORIES.TEXT_DOCUMENT,
|
||||
},
|
||||
{
|
||||
fileId: 'file-2',
|
||||
label: 'Image.png',
|
||||
fileCategory: FILE_CATEGORIES.IMAGE,
|
||||
},
|
||||
],
|
||||
[
|
||||
{
|
||||
fileId: 'file-3',
|
||||
label: 'Report.xlsx',
|
||||
fileCategory: FILE_CATEGORIES.SPREADSHEET,
|
||||
},
|
||||
],
|
||||
[],
|
||||
],
|
||||
} as const satisfies SettingsFieldTypeConfig<FieldFilesValue>,
|
||||
};
|
||||
|
||||
+4
@@ -53,6 +53,10 @@ export const SettingsDataModelFieldMaxValuesForm = ({
|
||||
title = t`Maximum values`;
|
||||
description = t`Limit how many values can be added to this field`;
|
||||
break;
|
||||
case FieldMetadataType.FILES:
|
||||
title = t`Maximum files`;
|
||||
description = t`Limit how many files can be attached to this field`;
|
||||
break;
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
|
||||
+8
@@ -117,6 +117,10 @@ const arrayFieldFormSchema = z
|
||||
.merge(mergeSettingsSchemas(settingsDataModelFieldMaxValuesSchema))
|
||||
.extend(isUniqueFieldFormSchema.shape);
|
||||
|
||||
const filesFieldFormSchema = z
|
||||
.object({ type: z.literal(FieldMetadataType.FILES) })
|
||||
.merge(mergeSettingsSchemas(settingsDataModelFieldMaxValuesSchema));
|
||||
|
||||
const otherFieldsFormSchema = z
|
||||
.object({
|
||||
type: z.enum(
|
||||
@@ -136,6 +140,7 @@ const otherFieldsFormSchema = z
|
||||
FieldMetadataType.EMAILS,
|
||||
FieldMetadataType.LINKS,
|
||||
FieldMetadataType.ARRAY,
|
||||
FieldMetadataType.FILES,
|
||||
]),
|
||||
) as [FieldMetadataType, ...FieldMetadataType[]],
|
||||
),
|
||||
@@ -159,6 +164,7 @@ export const settingsDataModelFieldSettingsFormSchema = z.discriminatedUnion(
|
||||
emailsFieldFormSchema,
|
||||
linksFieldFormSchema,
|
||||
arrayFieldFormSchema,
|
||||
filesFieldFormSchema,
|
||||
otherFieldsFormSchema,
|
||||
],
|
||||
);
|
||||
@@ -178,6 +184,7 @@ const previewableTypes = [
|
||||
FieldMetadataType.DATE,
|
||||
FieldMetadataType.DATE_TIME,
|
||||
FieldMetadataType.EMAILS,
|
||||
FieldMetadataType.FILES,
|
||||
FieldMetadataType.FULL_NAME,
|
||||
FieldMetadataType.LINKS,
|
||||
FieldMetadataType.MULTI_SELECT,
|
||||
@@ -325,6 +332,7 @@ export const SettingsDataModelFieldSettingsFormCard = ({
|
||||
FieldMetadataType.EMAILS,
|
||||
FieldMetadataType.LINKS,
|
||||
FieldMetadataType.ARRAY,
|
||||
FieldMetadataType.FILES,
|
||||
].includes(fieldType) && (
|
||||
<>
|
||||
<SettingsDataModelFieldMaxValuesForm
|
||||
|
||||
+5
-3
@@ -7,9 +7,11 @@ export const canBeUnique = (
|
||||
field: Pick<FieldMetadataItem, 'type' | 'isCustom'>,
|
||||
) => {
|
||||
if (
|
||||
[FieldMetadataType.MORPH_RELATION, FieldMetadataType.RELATION].includes(
|
||||
field.type,
|
||||
) ||
|
||||
[
|
||||
FieldMetadataType.MORPH_RELATION,
|
||||
FieldMetadataType.RELATION,
|
||||
FieldMetadataType.FILES,
|
||||
].includes(field.type) ||
|
||||
(isCompositeFieldType(field.type) &&
|
||||
SETTINGS_COMPOSITE_FIELD_TYPE_CONFIGS[field.type].subFields.every(
|
||||
(subField) => !subField.isIncludedInUniqueConstraint,
|
||||
|
||||
Reference in New Issue
Block a user