@ -1,10 +1,25 @@
|
||||
import { useMutation } from '@apollo/client';
|
||||
import { getOperationName } from '@apollo/client/utilities';
|
||||
|
||||
import { Currency, FieldMetadataType } from '~/generated-metadata/graphql';
|
||||
|
||||
import { ObjectMetadataItemIdentifier } from '../types/ObjectMetadataItemIdentifier';
|
||||
|
||||
import { useFindOneObjectMetadataItem } from './useFindOneObjectMetadataItem';
|
||||
|
||||
const defaultFieldValues: Record<FieldMetadataType, unknown> = {
|
||||
[FieldMetadataType.Money]: { amount: null, currency: Currency.Usd },
|
||||
[FieldMetadataType.Boolean]: false,
|
||||
[FieldMetadataType.Date]: null,
|
||||
[FieldMetadataType.Email]: '',
|
||||
[FieldMetadataType.Enum]: null,
|
||||
[FieldMetadataType.Number]: null,
|
||||
[FieldMetadataType.Phone]: '',
|
||||
[FieldMetadataType.Text]: '',
|
||||
[FieldMetadataType.Url]: { link: '', text: '' },
|
||||
[FieldMetadataType.Uuid]: '',
|
||||
};
|
||||
|
||||
export const useCreateOneObject = ({
|
||||
objectNamePlural,
|
||||
}: Pick<ObjectMetadataItemIdentifier, 'objectNamePlural'>) => {
|
||||
@ -21,10 +36,17 @@ export const useCreateOneObject = ({
|
||||
const [mutate] = useMutation(createOneMutation);
|
||||
|
||||
const createOneObject = foundObjectMetadataItem
|
||||
? (input: Record<string, any>) => {
|
||||
? (input: Record<string, unknown> = {}) => {
|
||||
return mutate({
|
||||
variables: {
|
||||
input: {
|
||||
...foundObjectMetadataItem.fields.reduce(
|
||||
(result, field) => ({
|
||||
...result,
|
||||
[field.name]: defaultFieldValues[field.type],
|
||||
}),
|
||||
{},
|
||||
),
|
||||
...input,
|
||||
},
|
||||
},
|
||||
|
||||
@ -4,12 +4,14 @@ import styled from '@emotion/styled';
|
||||
import { useRecoilState } from 'recoil';
|
||||
|
||||
import { useFindManyObjects } from '@/metadata/hooks/useFindManyObjects';
|
||||
import { parseFieldType } from '@/metadata/utils/parseFieldType';
|
||||
import { Tag } from '@/ui/display/tag/components/Tag';
|
||||
import { useLazyLoadIcon } from '@/ui/input/hooks/useLazyLoadIcon';
|
||||
import { FieldDisplay } from '@/ui/object/field/components/FieldDisplay';
|
||||
import { FieldContext } from '@/ui/object/field/contexts/FieldContext';
|
||||
import { BooleanFieldInput } from '@/ui/object/field/meta-types/input/components/BooleanFieldInput';
|
||||
import { entityFieldsFamilySelector } from '@/ui/object/field/states/selectors/entityFieldsFamilySelector';
|
||||
import { FieldMetadataType } from '~/generated/graphql';
|
||||
import { assertNotNull } from '~/utils/assert';
|
||||
|
||||
import { dataTypes } from '../constants/dataTypes';
|
||||
@ -137,7 +139,7 @@ export const SettingsObjectFieldPreview = ({
|
||||
value={{
|
||||
entityId: objects[0]?.id ?? objectNamePlural,
|
||||
fieldDefinition: {
|
||||
type: fieldType,
|
||||
type: parseFieldType(fieldType as FieldMetadataType),
|
||||
Icon: FieldIcon,
|
||||
fieldId: '',
|
||||
label: fieldLabel,
|
||||
|
||||
@ -64,7 +64,7 @@ export const SettingsObjectFieldTypeSelectSection = ({
|
||||
}),
|
||||
)}
|
||||
/>
|
||||
{['BOOLEAN', 'NUMBER', 'TEXT'].includes(fieldType) && (
|
||||
{['BOOLEAN', 'MONEY', 'NUMBER', 'TEXT'].includes(fieldType) && (
|
||||
<StyledSettingsObjectFieldTypeCard
|
||||
preview={
|
||||
<SettingsObjectFieldPreview
|
||||
|
||||
@ -24,14 +24,6 @@ type Story = StoryObj<typeof SettingsObjectFieldPreview>;
|
||||
|
||||
export const Text: Story = {};
|
||||
|
||||
export const Number: Story = {
|
||||
args: {
|
||||
fieldIconKey: 'IconUsers',
|
||||
fieldLabel: 'Employees',
|
||||
fieldType: 'NUMBER',
|
||||
},
|
||||
};
|
||||
|
||||
export const Boolean: Story = {
|
||||
args: {
|
||||
fieldIconKey: 'IconHeadphones',
|
||||
@ -40,6 +32,22 @@ export const Boolean: Story = {
|
||||
},
|
||||
};
|
||||
|
||||
export const Currency: Story = {
|
||||
args: {
|
||||
fieldIconKey: 'IconCurrencyDollar',
|
||||
fieldLabel: 'Amount',
|
||||
fieldType: 'MONEY',
|
||||
},
|
||||
};
|
||||
|
||||
export const Number: Story = {
|
||||
args: {
|
||||
fieldIconKey: 'IconUsers',
|
||||
fieldLabel: 'Employees',
|
||||
fieldType: 'NUMBER',
|
||||
},
|
||||
};
|
||||
|
||||
export const CustomObject: Story = {
|
||||
args: {
|
||||
isObjectCustom: true,
|
||||
|
||||
@ -1,11 +1,13 @@
|
||||
import {
|
||||
IconCheck,
|
||||
IconCoins,
|
||||
IconLink,
|
||||
IconNumbers,
|
||||
IconPlug,
|
||||
IconTextSize,
|
||||
} from '@/ui/display/icon';
|
||||
import { IconComponent } from '@/ui/display/icon/types/IconComponent';
|
||||
import { Currency } from '~/generated-metadata/graphql';
|
||||
|
||||
import { MetadataFieldDataType } from '../types/ObjectFieldDataType';
|
||||
|
||||
@ -13,7 +15,14 @@ export const dataTypes: Record<
|
||||
MetadataFieldDataType,
|
||||
{ label: string; Icon: IconComponent; defaultValue?: unknown }
|
||||
> = {
|
||||
BOOLEAN: { label: 'True/False', Icon: IconCheck, defaultValue: true },
|
||||
MONEY: {
|
||||
label: 'Currency',
|
||||
Icon: IconCoins,
|
||||
defaultValue: { amount: 2000, currency: Currency.Usd },
|
||||
},
|
||||
NUMBER: { label: 'Number', Icon: IconNumbers, defaultValue: 2000 },
|
||||
RELATION: { label: 'Relation', Icon: IconPlug },
|
||||
TEXT: {
|
||||
label: 'Text',
|
||||
Icon: IconTextSize,
|
||||
@ -21,6 +30,4 @@ export const dataTypes: Record<
|
||||
'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vestibulum magna enim, dapibus non enim in, lacinia faucibus nunc. Sed interdum ante sed felis facilisis, eget ultricies neque molestie. Mauris auctor, justo eu volutpat cursus, libero erat tempus nulla, non sodales lorem lacus a est.',
|
||||
},
|
||||
URL: { label: 'Link', Icon: IconLink },
|
||||
BOOLEAN: { label: 'True/False', Icon: IconCheck, defaultValue: true },
|
||||
RELATION: { label: 'Relation', Icon: IconPlug },
|
||||
};
|
||||
|
||||
@ -7,6 +7,7 @@ import { TableCell } from '@/ui/layout/table/components/TableCell';
|
||||
import { TableRow } from '@/ui/layout/table/components/TableRow';
|
||||
import { Field } from '~/generated-metadata/graphql';
|
||||
|
||||
import { dataTypes } from '../../constants/dataTypes';
|
||||
import { MetadataFieldDataType } from '../../types/ObjectFieldDataType';
|
||||
|
||||
import { SettingsObjectFieldDataType } from './SettingsObjectFieldDataType';
|
||||
@ -30,6 +31,9 @@ const StyledIconTableCell = styled(TableCell)`
|
||||
padding-right: ${({ theme }) => theme.spacing(1)};
|
||||
`;
|
||||
|
||||
// TODO: remove "relation" type for now, add it back when the backend is ready.
|
||||
const { RELATION: _, ...dataTypesWithoutRelation } = dataTypes;
|
||||
|
||||
export const SettingsObjectFieldItemTableRow = ({
|
||||
ActionIcon,
|
||||
fieldItem,
|
||||
@ -38,15 +42,12 @@ export const SettingsObjectFieldItemTableRow = ({
|
||||
const { Icon } = useLazyLoadIcon(fieldItem.icon ?? '');
|
||||
|
||||
// TODO: parse with zod and merge types with FieldType (create a subset of FieldType for example)
|
||||
const fieldDataTypeIsSupported = [
|
||||
'TEXT',
|
||||
'NUMBER',
|
||||
'BOOLEAN',
|
||||
'URL',
|
||||
].includes(fieldItem.type);
|
||||
const fieldDataTypeIsSupported = Object.keys(
|
||||
dataTypesWithoutRelation,
|
||||
).includes(fieldItem.type);
|
||||
|
||||
if (!fieldDataTypeIsSupported) {
|
||||
return <></>;
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
export type MetadataFieldDataType =
|
||||
| 'BOOLEAN'
|
||||
| 'MONEY'
|
||||
| 'NUMBER'
|
||||
| 'RELATION'
|
||||
| 'TEXT'
|
||||
|
||||
@ -29,6 +29,7 @@ export {
|
||||
IconChevronsRight,
|
||||
IconChevronUp,
|
||||
IconCircleDot,
|
||||
IconCoins,
|
||||
IconColorSwatch,
|
||||
IconMessageCircle as IconComment,
|
||||
IconCopy,
|
||||
|
||||
@ -6,6 +6,7 @@ import { useFieldInitialValue } from '../../hooks/useFieldInitialValue';
|
||||
import { entityFieldsFamilySelector } from '../../states/selectors/entityFieldsFamilySelector';
|
||||
import { assertFieldMetadata } from '../../types/guards/assertFieldMetadata';
|
||||
import { isFieldText } from '../../types/guards/isFieldText';
|
||||
import { isFieldTextValue } from '../../types/guards/isFieldTextValue';
|
||||
|
||||
export const useTextField = () => {
|
||||
const { entityId, fieldDefinition, hotkeyScope } = useContext(FieldContext);
|
||||
@ -20,16 +21,17 @@ export const useTextField = () => {
|
||||
fieldName: fieldName,
|
||||
}),
|
||||
);
|
||||
const fieldTextValue = isFieldTextValue(fieldValue) ? fieldValue : '';
|
||||
|
||||
const fieldInitialValue = useFieldInitialValue();
|
||||
|
||||
const initialValue = fieldInitialValue?.isEmpty
|
||||
? ''
|
||||
: fieldInitialValue?.value ?? fieldValue;
|
||||
: fieldInitialValue?.value ?? fieldTextValue;
|
||||
|
||||
return {
|
||||
fieldDefinition,
|
||||
fieldValue,
|
||||
fieldValue: fieldTextValue,
|
||||
initialValue,
|
||||
setFieldValue,
|
||||
hotkeyScope,
|
||||
|
||||
@ -1,5 +1,7 @@
|
||||
import { selectorFamily } from 'recoil';
|
||||
|
||||
import { assertNotNull } from '~/utils/assert';
|
||||
|
||||
import { FieldDefinition } from '../../types/FieldDefinition';
|
||||
import { FieldMetadata } from '../../types/FieldMetadata';
|
||||
import { isFieldBoolean } from '../../types/guards/isFieldBoolean';
|
||||
@ -8,6 +10,8 @@ import { isFieldDate } from '../../types/guards/isFieldDate';
|
||||
import { isFieldDoubleTextChip } from '../../types/guards/isFieldDoubleTextChip';
|
||||
import { isFieldEmail } from '../../types/guards/isFieldEmail';
|
||||
import { isFieldMoney } from '../../types/guards/isFieldMoney';
|
||||
import { isFieldMoneyAmountV2 } from '../../types/guards/isFieldMoneyAmountV2';
|
||||
import { isFieldMoneyAmountV2Value } from '../../types/guards/isFieldMoneyAmountV2Value';
|
||||
import { isFieldNumber } from '../../types/guards/isFieldNumber';
|
||||
import { isFieldPhone } from '../../types/guards/isFieldPhone';
|
||||
import { isFieldProbability } from '../../types/guards/isFieldProbability';
|
||||
@ -17,6 +21,8 @@ import { isFieldText } from '../../types/guards/isFieldText';
|
||||
import { isFieldURL } from '../../types/guards/isFieldURL';
|
||||
import { entityFieldsFamilyState } from '../entityFieldsFamilyState';
|
||||
|
||||
const isValueEmpty = (value: unknown) => !assertNotNull(value) || value === '';
|
||||
|
||||
export const isEntityFieldEmptyFamilySelector = selectorFamily({
|
||||
key: 'isEntityFieldEmptyFamilySelector',
|
||||
get: ({
|
||||
@ -44,32 +50,30 @@ export const isEntityFieldEmptyFamilySelector = selectorFamily({
|
||||
const fieldName = fieldDefinition.metadata.fieldName;
|
||||
const fieldValue = get(entityFieldsFamilyState(entityId))?.[
|
||||
fieldName
|
||||
] as string | null;
|
||||
] as string | number | boolean | null;
|
||||
|
||||
return (
|
||||
fieldValue === null || fieldValue === undefined || fieldValue === ''
|
||||
);
|
||||
} else if (isFieldRelation(fieldDefinition)) {
|
||||
return isValueEmpty(fieldValue);
|
||||
}
|
||||
|
||||
if (isFieldRelation(fieldDefinition)) {
|
||||
const fieldName = fieldDefinition.metadata.fieldName;
|
||||
|
||||
const fieldValue = get(entityFieldsFamilyState(entityId))?.[fieldName];
|
||||
|
||||
if (isFieldRelationValue(fieldValue)) {
|
||||
return fieldValue === null || fieldValue === undefined;
|
||||
}
|
||||
} else if (isFieldChip(fieldDefinition)) {
|
||||
return isFieldRelationValue(fieldValue) && isValueEmpty(fieldValue);
|
||||
}
|
||||
|
||||
if (isFieldChip(fieldDefinition)) {
|
||||
const contentFieldName = fieldDefinition.metadata.contentFieldName;
|
||||
|
||||
const contentFieldValue = get(entityFieldsFamilyState(entityId))?.[
|
||||
contentFieldName
|
||||
] as string | null;
|
||||
|
||||
return (
|
||||
contentFieldValue === null ||
|
||||
contentFieldValue === undefined ||
|
||||
contentFieldValue === ''
|
||||
);
|
||||
} else if (isFieldDoubleTextChip(fieldDefinition)) {
|
||||
return isValueEmpty(contentFieldValue);
|
||||
}
|
||||
|
||||
if (isFieldDoubleTextChip(fieldDefinition)) {
|
||||
const firstValueFieldName =
|
||||
fieldDefinition.metadata.firstValueFieldName;
|
||||
|
||||
@ -85,20 +89,24 @@ export const isEntityFieldEmptyFamilySelector = selectorFamily({
|
||||
)?.[secondValueFieldName] as string | null;
|
||||
|
||||
return (
|
||||
(contentFieldFirstValue === null ||
|
||||
contentFieldFirstValue === undefined ||
|
||||
contentFieldFirstValue === '') &&
|
||||
(contentFieldSecondValue === null ||
|
||||
contentFieldSecondValue === undefined ||
|
||||
contentFieldSecondValue === '')
|
||||
);
|
||||
} else {
|
||||
throw new Error(
|
||||
`Entity field type not supported in isEntityFieldEmptyFamilySelector : ${fieldDefinition.type}}`,
|
||||
isValueEmpty(contentFieldFirstValue) &&
|
||||
isValueEmpty(contentFieldSecondValue)
|
||||
);
|
||||
}
|
||||
|
||||
return false;
|
||||
if (isFieldMoneyAmountV2(fieldDefinition)) {
|
||||
const fieldName = fieldDefinition.metadata.fieldName;
|
||||
const fieldValue = get(entityFieldsFamilyState(entityId))?.[fieldName];
|
||||
|
||||
return (
|
||||
!isFieldMoneyAmountV2Value(fieldValue) ||
|
||||
isValueEmpty(fieldValue?.amount)
|
||||
);
|
||||
}
|
||||
|
||||
throw new Error(
|
||||
`Entity field type not supported in isEntityFieldEmptyFamilySelector : ${fieldDefinition.type}}`,
|
||||
);
|
||||
};
|
||||
},
|
||||
});
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
import { z } from 'zod';
|
||||
|
||||
import { FieldMoneyValue } from '../FieldMetadata';
|
||||
import { FieldMoneyAmountV2Value } from '../FieldMetadata';
|
||||
|
||||
const moneyAmountV2Schema = z.object({
|
||||
currency: z.string(),
|
||||
@ -9,5 +9,5 @@ const moneyAmountV2Schema = z.object({
|
||||
|
||||
export const isFieldMoneyAmountV2Value = (
|
||||
fieldValue: unknown,
|
||||
): fieldValue is FieldMoneyValue =>
|
||||
): fieldValue is FieldMoneyAmountV2Value =>
|
||||
moneyAmountV2Schema.safeParse(fieldValue).success;
|
||||
|
||||
@ -16,6 +16,7 @@ export const moneyObjectDefinition = {
|
||||
name: 'amount',
|
||||
label: 'Amount',
|
||||
targetColumnMap: { value: 'amount' },
|
||||
isNullable: true,
|
||||
},
|
||||
{
|
||||
id: 'currency',
|
||||
|
||||
Reference in New Issue
Block a user