feat: expose foreign key (#2505)
* fix: typo * feat: expose foreign key * fix: foreign key exposition * fix: be able to filter by foreign key * feat: add `isSystem` on field metadata * feat: update all seeds * fix: seed issues * fix: sync metadata generated files * fix: squash metadata migrations * Fix conflicts --------- Co-authored-by: Charles Bochet <charles@twenty.com>
This commit is contained in:
@ -8,7 +8,10 @@ import { useFindManyObjectMetadataItems } from '../hooks/useFindManyObjectMetada
|
||||
|
||||
export const ObjectMetadataNavItems = () => {
|
||||
const { objectMetadataItems } = useFindManyObjectMetadataItems({
|
||||
filter: {
|
||||
objectFilter: {
|
||||
isSystem: { is: false },
|
||||
},
|
||||
fieldFilter: {
|
||||
isSystem: { is: false },
|
||||
},
|
||||
});
|
||||
|
||||
@ -1,8 +1,11 @@
|
||||
import { gql } from '@apollo/client';
|
||||
|
||||
export const FIND_MANY_METADATA_OBJECTS = gql`
|
||||
query ObjectMetadataItems($filter: objectFilter) {
|
||||
objects(paging: { first: 1000 }, filter: $filter) {
|
||||
query ObjectMetadataItems(
|
||||
$objectFilter: objectFilter
|
||||
$fieldFilter: fieldFilter
|
||||
) {
|
||||
objects(paging: { first: 1000 }, filter: $objectFilter) {
|
||||
edges {
|
||||
node {
|
||||
id
|
||||
@ -18,7 +21,7 @@ export const FIND_MANY_METADATA_OBJECTS = gql`
|
||||
isSystem
|
||||
createdAt
|
||||
updatedAt
|
||||
fields(paging: { first: 1000 }) {
|
||||
fields(paging: { first: 1000 }, filter: $fieldFilter) {
|
||||
edges {
|
||||
node {
|
||||
id
|
||||
@ -29,6 +32,7 @@ export const FIND_MANY_METADATA_OBJECTS = gql`
|
||||
icon
|
||||
isCustom
|
||||
isActive
|
||||
isSystem
|
||||
isNullable
|
||||
createdAt
|
||||
updatedAt
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
import { MetadataFieldDataType } from '@/settings/data-model/types/ObjectFieldDataType';
|
||||
import { FieldType } from '@/ui/object/field/types/FieldType';
|
||||
import { Field } from '~/generated/graphql';
|
||||
import { FieldMetadataType } from '~/generated-metadata/graphql';
|
||||
|
||||
import { FieldMetadataItem } from '../types/FieldMetadataItem';
|
||||
import { formatFieldMetadataItemInput } from '../utils/formatFieldMetadataItemInput';
|
||||
@ -16,13 +17,13 @@ export const useFieldMetadataItem = () => {
|
||||
const createMetadataField = (
|
||||
input: Pick<Field, 'label' | 'icon' | 'description'> & {
|
||||
objectMetadataId: string;
|
||||
type: MetadataFieldDataType;
|
||||
type: FieldMetadataType;
|
||||
},
|
||||
) =>
|
||||
createOneFieldMetadataItem({
|
||||
...formatFieldMetadataItemInput(input),
|
||||
objectMetadataId: input.objectMetadataId,
|
||||
type: input.type,
|
||||
type: input.type as FieldType,
|
||||
});
|
||||
|
||||
const editMetadataField = (
|
||||
|
||||
@ -3,6 +3,7 @@ import { useQuery } from '@apollo/client';
|
||||
|
||||
import { useSnackBar } from '@/ui/feedback/snack-bar/hooks/useSnackBar';
|
||||
import {
|
||||
FieldFilter,
|
||||
ObjectFilter,
|
||||
ObjectMetadataItemsQuery,
|
||||
ObjectMetadataItemsQueryVariables,
|
||||
@ -17,8 +18,13 @@ import { useApolloMetadataClient } from './useApolloMetadataClient';
|
||||
// TODO: test fetchMore
|
||||
export const useFindManyObjectMetadataItems = ({
|
||||
skip,
|
||||
filter,
|
||||
}: { skip?: boolean; filter?: ObjectFilter } = {}) => {
|
||||
objectFilter,
|
||||
fieldFilter,
|
||||
}: {
|
||||
skip?: boolean;
|
||||
objectFilter?: ObjectFilter;
|
||||
fieldFilter?: FieldFilter;
|
||||
} = {}) => {
|
||||
const apolloMetadataClient = useApolloMetadataClient();
|
||||
|
||||
const { enqueueSnackBar } = useSnackBar();
|
||||
@ -32,7 +38,8 @@ export const useFindManyObjectMetadataItems = ({
|
||||
FIND_MANY_METADATA_OBJECTS,
|
||||
{
|
||||
variables: {
|
||||
filter,
|
||||
objectFilter,
|
||||
fieldFilter,
|
||||
},
|
||||
client: apolloMetadataClient ?? undefined,
|
||||
skip: skip || !apolloMetadataClient,
|
||||
|
||||
@ -32,7 +32,7 @@ export const useFindManyObjectMetadataItems = ({
|
||||
FIND_MANY_METADATA_OBJECTS,
|
||||
{
|
||||
variables: {
|
||||
filter,
|
||||
objectFilter: filter,
|
||||
},
|
||||
client: apolloMetadataClient ?? undefined,
|
||||
skip: skip || !apolloMetadataClient,
|
||||
|
||||
@ -9,7 +9,10 @@ import { useUpdateOneObjectMetadataItem } from './useUpdateOneObjectMetadataItem
|
||||
|
||||
export const useObjectMetadataItemForSettings = () => {
|
||||
const { objectMetadataItems, loading } = useFindManyObjectMetadataItems({
|
||||
filter: {
|
||||
objectFilter: {
|
||||
isSystem: { is: false },
|
||||
},
|
||||
fieldFilter: {
|
||||
isSystem: { is: false },
|
||||
},
|
||||
});
|
||||
|
||||
@ -15,13 +15,12 @@ import { FieldMetadataType } from '~/generated/graphql';
|
||||
import { assertNotNull } from '~/utils/assert';
|
||||
|
||||
import { dataTypes } from '../constants/dataTypes';
|
||||
import { MetadataFieldDataType } from '../types/ObjectFieldDataType';
|
||||
|
||||
export type SettingsObjectFieldPreviewProps = {
|
||||
fieldIconKey?: string | null;
|
||||
fieldLabel: string;
|
||||
fieldName?: string;
|
||||
fieldType: MetadataFieldDataType;
|
||||
fieldType: FieldMetadataType;
|
||||
isObjectCustom: boolean;
|
||||
objectIconKey?: string | null;
|
||||
objectLabelPlural: string;
|
||||
|
||||
@ -3,9 +3,9 @@ import styled from '@emotion/styled';
|
||||
import { H2Title } from '@/ui/display/typography/components/H2Title';
|
||||
import { Select } from '@/ui/input/components/Select';
|
||||
import { Section } from '@/ui/layout/section/components/Section';
|
||||
import { FieldMetadataType } from '~/generated-metadata/graphql';
|
||||
|
||||
import { dataTypes } from '../constants/dataTypes';
|
||||
import { MetadataFieldDataType } from '../types/ObjectFieldDataType';
|
||||
|
||||
import {
|
||||
SettingsObjectFieldPreview,
|
||||
@ -15,7 +15,7 @@ import { SettingsObjectFieldTypeCard } from './SettingsObjectFieldTypeCard';
|
||||
|
||||
type SettingsObjectFieldTypeSelectSectionProps = {
|
||||
disabled?: boolean;
|
||||
onChange?: (value: MetadataFieldDataType) => void;
|
||||
onChange?: (value: FieldMetadataType) => void;
|
||||
} & Pick<
|
||||
SettingsObjectFieldPreviewProps,
|
||||
| 'fieldIconKey'
|
||||
@ -59,7 +59,7 @@ export const SettingsObjectFieldTypeSelectSection = ({
|
||||
onChange={onChange}
|
||||
options={Object.entries(dataTypesWithoutRelation).map(
|
||||
([key, dataType]) => ({
|
||||
value: key as MetadataFieldDataType,
|
||||
value: key as FieldMetadataType,
|
||||
...dataType,
|
||||
}),
|
||||
)}
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
import { MemoryRouter } from 'react-router-dom';
|
||||
import { Meta, StoryObj } from '@storybook/react';
|
||||
|
||||
import { FieldMetadataType } from '~/generated-metadata/graphql';
|
||||
import { ComponentDecorator } from '~/testing/decorators/ComponentDecorator';
|
||||
|
||||
import { SettingsObjectFieldPreview } from '../SettingsObjectFieldPreview';
|
||||
@ -12,7 +13,7 @@ const meta: Meta<typeof SettingsObjectFieldPreview> = {
|
||||
args: {
|
||||
fieldIconKey: 'IconNotes',
|
||||
fieldLabel: 'Description',
|
||||
fieldType: 'TEXT',
|
||||
fieldType: FieldMetadataType.Text,
|
||||
isObjectCustom: false,
|
||||
objectIconKey: 'IconBuildingSkyscraper',
|
||||
objectLabelPlural: 'Companies',
|
||||
@ -29,7 +30,7 @@ export const Boolean: Story = {
|
||||
args: {
|
||||
fieldIconKey: 'IconHeadphones',
|
||||
fieldLabel: 'Priority Support',
|
||||
fieldType: 'BOOLEAN',
|
||||
fieldType: FieldMetadataType.Boolean,
|
||||
},
|
||||
};
|
||||
|
||||
@ -37,7 +38,7 @@ export const Currency: Story = {
|
||||
args: {
|
||||
fieldIconKey: 'IconCurrencyDollar',
|
||||
fieldLabel: 'Amount',
|
||||
fieldType: 'MONEY',
|
||||
fieldType: FieldMetadataType.Money,
|
||||
},
|
||||
};
|
||||
|
||||
@ -45,7 +46,7 @@ export const Date: Story = {
|
||||
args: {
|
||||
fieldIconKey: 'IconCalendarEvent',
|
||||
fieldLabel: 'Registration Date',
|
||||
fieldType: 'DATE',
|
||||
fieldType: FieldMetadataType.Date,
|
||||
},
|
||||
};
|
||||
|
||||
@ -60,7 +61,7 @@ export const Link: Story = {
|
||||
args: {
|
||||
fieldIconKey: 'IconWorldWww',
|
||||
fieldLabel: 'Website',
|
||||
fieldType: 'URL',
|
||||
fieldType: FieldMetadataType.Url,
|
||||
},
|
||||
};
|
||||
|
||||
@ -68,7 +69,7 @@ export const Number: Story = {
|
||||
args: {
|
||||
fieldIconKey: 'IconUsers',
|
||||
fieldLabel: 'Employees',
|
||||
fieldType: 'NUMBER',
|
||||
fieldType: FieldMetadataType.Number,
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
import { Meta, StoryObj } from '@storybook/react';
|
||||
|
||||
import { TextInput } from '@/ui/input/components/TextInput';
|
||||
import { FieldMetadataType } from '~/generated/graphql';
|
||||
import { ComponentDecorator } from '~/testing/decorators/ComponentDecorator';
|
||||
|
||||
import { SettingsObjectFieldPreview } from '../SettingsObjectFieldPreview';
|
||||
@ -15,7 +16,7 @@ const meta: Meta<typeof SettingsObjectFieldTypeCard> = {
|
||||
<SettingsObjectFieldPreview
|
||||
fieldIconKey="IconNotes"
|
||||
fieldLabel="Description"
|
||||
fieldType="TEXT"
|
||||
fieldType={FieldMetadataType.Text}
|
||||
isObjectCustom={false}
|
||||
objectIconKey="IconUser"
|
||||
objectLabelPlural="People"
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
import { Meta, StoryObj } from '@storybook/react';
|
||||
import { userEvent, within } from '@storybook/testing-library';
|
||||
|
||||
import { FieldMetadataType } from '~/generated-metadata/graphql';
|
||||
import { ComponentDecorator } from '~/testing/decorators/ComponentDecorator';
|
||||
|
||||
import { SettingsObjectFieldTypeSelectSection } from '../SettingsObjectFieldTypeSelectSection';
|
||||
@ -10,7 +11,7 @@ const meta: Meta<typeof SettingsObjectFieldTypeSelectSection> = {
|
||||
component: SettingsObjectFieldTypeSelectSection,
|
||||
decorators: [ComponentDecorator],
|
||||
args: {
|
||||
fieldType: 'NUMBER',
|
||||
fieldType: FieldMetadataType.Number,
|
||||
fieldIconKey: 'IconUsers',
|
||||
fieldLabel: 'Employees',
|
||||
fieldName: 'employees',
|
||||
|
||||
@ -2,45 +2,67 @@ import {
|
||||
IconCalendarEvent,
|
||||
IconCheck,
|
||||
IconCoins,
|
||||
IconKey,
|
||||
IconLink,
|
||||
IconMail,
|
||||
IconNumbers,
|
||||
IconPhone,
|
||||
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';
|
||||
import { Currency, FieldMetadataType } from '~/generated-metadata/graphql';
|
||||
|
||||
const defaultDateValue = new Date();
|
||||
defaultDateValue.setFullYear(defaultDateValue.getFullYear() + 2);
|
||||
|
||||
export const dataTypes: Record<
|
||||
MetadataFieldDataType,
|
||||
FieldMetadataType,
|
||||
{ label: string; Icon: IconComponent; defaultValue?: unknown }
|
||||
> = {
|
||||
TEXT: {
|
||||
[FieldMetadataType.Uuid]: {
|
||||
label: 'Unique ID',
|
||||
Icon: IconKey,
|
||||
defaultValue: '00000000-0000-0000-0000-000000000000',
|
||||
},
|
||||
[FieldMetadataType.Text]: {
|
||||
label: 'Text',
|
||||
Icon: IconTextSize,
|
||||
defaultValue:
|
||||
'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.',
|
||||
},
|
||||
NUMBER: { label: 'Number', Icon: IconNumbers, defaultValue: 2000 },
|
||||
URL: {
|
||||
[FieldMetadataType.Number]: {
|
||||
label: 'Number',
|
||||
Icon: IconNumbers,
|
||||
defaultValue: 2000,
|
||||
},
|
||||
[FieldMetadataType.Url]: {
|
||||
label: 'Link',
|
||||
Icon: IconLink,
|
||||
defaultValue: { link: 'www.twenty.com', text: '' },
|
||||
},
|
||||
BOOLEAN: { label: 'True/False', Icon: IconCheck, defaultValue: true },
|
||||
DATE: {
|
||||
[FieldMetadataType.Boolean]: {
|
||||
label: 'True/False',
|
||||
Icon: IconCheck,
|
||||
defaultValue: true,
|
||||
},
|
||||
[FieldMetadataType.Date]: {
|
||||
label: 'Date',
|
||||
Icon: IconCalendarEvent,
|
||||
defaultValue: defaultDateValue.toISOString(),
|
||||
},
|
||||
MONEY: {
|
||||
[FieldMetadataType.Money]: {
|
||||
label: 'Currency',
|
||||
Icon: IconCoins,
|
||||
defaultValue: { amount: 2000, currency: Currency.Usd },
|
||||
},
|
||||
RELATION: { label: 'Relation', Icon: IconPlug },
|
||||
[FieldMetadataType.Relation]: { label: 'Relation', Icon: IconPlug },
|
||||
[FieldMetadataType.Email]: { label: 'Email', Icon: IconMail },
|
||||
[FieldMetadataType.Phone]: { label: 'Phone', Icon: IconPhone },
|
||||
[FieldMetadataType.Probability]: {
|
||||
label: 'Probability',
|
||||
Icon: IconNumbers,
|
||||
defaultValue: 50,
|
||||
},
|
||||
[FieldMetadataType.Enum]: { label: 'Enum', Icon: IconPlug },
|
||||
};
|
||||
|
||||
@ -1,10 +1,11 @@
|
||||
import { css, useTheme } from '@emotion/react';
|
||||
import styled from '@emotion/styled';
|
||||
|
||||
import { dataTypes } from '../../constants/dataTypes';
|
||||
import { MetadataFieldDataType } from '../../types/ObjectFieldDataType';
|
||||
import { FieldMetadataType } from '~/generated-metadata/graphql';
|
||||
|
||||
const StyledDataType = styled.div<{ value: MetadataFieldDataType }>`
|
||||
import { dataTypes } from '../../constants/dataTypes';
|
||||
|
||||
const StyledDataType = styled.div<{ value: FieldMetadataType }>`
|
||||
align-items: center;
|
||||
border: 1px solid transparent;
|
||||
border-radius: ${({ theme }) => theme.border.radius.sm};
|
||||
@ -24,7 +25,7 @@ const StyledDataType = styled.div<{ value: MetadataFieldDataType }>`
|
||||
`;
|
||||
|
||||
type SettingsObjectFieldDataTypeProps = {
|
||||
value: MetadataFieldDataType;
|
||||
value: FieldMetadataType;
|
||||
};
|
||||
|
||||
export const SettingsObjectFieldDataType = ({
|
||||
|
||||
@ -6,9 +6,9 @@ import { FieldMetadataItem } from '@/object-metadata/types/FieldMetadataItem';
|
||||
import { useLazyLoadIcon } from '@/ui/input/hooks/useLazyLoadIcon';
|
||||
import { TableCell } from '@/ui/layout/table/components/TableCell';
|
||||
import { TableRow } from '@/ui/layout/table/components/TableRow';
|
||||
import { FieldMetadataType } from '~/generated-metadata/graphql';
|
||||
|
||||
import { dataTypes } from '../../constants/dataTypes';
|
||||
import { MetadataFieldDataType } from '../../types/ObjectFieldDataType';
|
||||
|
||||
import { SettingsObjectFieldDataType } from './SettingsObjectFieldDataType';
|
||||
|
||||
@ -59,7 +59,7 @@ export const SettingsObjectFieldItemTableRow = ({
|
||||
<TableCell>{fieldItem.isCustom ? 'Custom' : 'Standard'}</TableCell>
|
||||
<TableCell>
|
||||
<SettingsObjectFieldDataType
|
||||
value={fieldItem.type as MetadataFieldDataType}
|
||||
value={fieldItem.type as FieldMetadataType}
|
||||
/>
|
||||
</TableCell>
|
||||
<StyledIconTableCell>{ActionIcon}</StyledIconTableCell>
|
||||
|
||||
@ -47,6 +47,7 @@ export {
|
||||
IconHeartOff,
|
||||
IconHelpCircle,
|
||||
IconHierarchy2,
|
||||
IconKey,
|
||||
IconLanguage,
|
||||
IconLayoutKanban,
|
||||
IconLayoutSidebarLeftCollapse,
|
||||
|
||||
Reference in New Issue
Block a user