Split components into object-metadata and object-record (#2425)
* Split components into object-metadata and object-record * Fix seed
This commit is contained in:
@ -0,0 +1,37 @@
|
||||
/* eslint-disable no-console */
|
||||
import { useMemo } from 'react';
|
||||
import { ApolloClient, InMemoryCache } from '@apollo/client';
|
||||
import { useRecoilState } from 'recoil';
|
||||
|
||||
import { tokenPairState } from '@/auth/states/tokenPairState';
|
||||
import { REACT_APP_SERVER_BASE_URL } from '~/config';
|
||||
|
||||
import { ApolloMetadataClientContext } from '../context/ApolloClientMetadataContext';
|
||||
|
||||
export const ApolloMetadataClientProvider = ({
|
||||
children,
|
||||
}: {
|
||||
children: React.ReactNode;
|
||||
}) => {
|
||||
const [tokenPair] = useRecoilState(tokenPairState);
|
||||
|
||||
const apolloMetadataClient = useMemo(() => {
|
||||
if (tokenPair?.accessToken.token) {
|
||||
return new ApolloClient({
|
||||
uri: `${REACT_APP_SERVER_BASE_URL}/metadata`,
|
||||
cache: new InMemoryCache(),
|
||||
headers: {
|
||||
Authorization: `Bearer ${tokenPair.accessToken.token}`,
|
||||
},
|
||||
});
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}, [tokenPair]);
|
||||
|
||||
return (
|
||||
<ApolloMetadataClientContext.Provider value={apolloMetadataClient}>
|
||||
{children}
|
||||
</ApolloMetadataClientContext.Provider>
|
||||
);
|
||||
};
|
||||
@ -0,0 +1,42 @@
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
|
||||
import { Icon123 } from '@/ui/input/constants/icons';
|
||||
import { useLazyLoadIcons } from '@/ui/input/hooks/useLazyLoadIcons';
|
||||
import NavItem from '@/ui/navigation/navbar/components/NavItem';
|
||||
|
||||
import { useFindManyObjectMetadataItems } from '../hooks/useFindManyObjectMetadataItems';
|
||||
|
||||
export const ObjectMetadataNavItems = () => {
|
||||
const { objectMetadataItems } = useFindManyObjectMetadataItems();
|
||||
|
||||
const navigate = useNavigate();
|
||||
const { icons } = useLazyLoadIcons();
|
||||
|
||||
return (
|
||||
<>
|
||||
{objectMetadataItems
|
||||
.filter(
|
||||
(objectMetadataItem) =>
|
||||
objectMetadataItem.isActive &&
|
||||
!objectMetadataItem.namePlural.endsWith('V2'),
|
||||
)
|
||||
.map((objectMetadataItem) => {
|
||||
return (
|
||||
<NavItem
|
||||
key={objectMetadataItem.id}
|
||||
label={objectMetadataItem.labelPlural}
|
||||
to={`/objects/${objectMetadataItem.namePlural}`}
|
||||
Icon={
|
||||
objectMetadataItem.icon
|
||||
? icons[objectMetadataItem.icon]
|
||||
: Icon123
|
||||
}
|
||||
onClick={() => {
|
||||
navigate(`/objects/${objectMetadataItem.namePlural}`);
|
||||
}}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
</>
|
||||
);
|
||||
};
|
||||
@ -0,0 +1,5 @@
|
||||
import { createContext } from 'react';
|
||||
import { ApolloClient, NormalizedCacheObject } from '@apollo/client';
|
||||
|
||||
export const ApolloMetadataClientContext =
|
||||
createContext<ApolloClient<NormalizedCacheObject> | null>(null);
|
||||
121
front/src/modules/object-metadata/graphql/mutations.ts
Normal file
121
front/src/modules/object-metadata/graphql/mutations.ts
Normal file
@ -0,0 +1,121 @@
|
||||
import { gql } from '@apollo/client';
|
||||
|
||||
export const CREATE_ONE_METADATA_OBJECT = gql`
|
||||
mutation CreateOneObjectMetadataItem($input: CreateOneObjectInput!) {
|
||||
createOneObject(input: $input) {
|
||||
id
|
||||
dataSourceId
|
||||
nameSingular
|
||||
namePlural
|
||||
labelSingular
|
||||
labelPlural
|
||||
description
|
||||
icon
|
||||
isCustom
|
||||
isActive
|
||||
createdAt
|
||||
updatedAt
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
export const CREATE_ONE_METADATA_FIELD = gql`
|
||||
mutation CreateOneFieldMetadataItem($input: CreateOneFieldInput!) {
|
||||
createOneField(input: $input) {
|
||||
id
|
||||
type
|
||||
name
|
||||
label
|
||||
description
|
||||
icon
|
||||
placeholder
|
||||
isCustom
|
||||
isActive
|
||||
isNullable
|
||||
createdAt
|
||||
updatedAt
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
export const UPDATE_ONE_METADATA_FIELD = gql`
|
||||
mutation UpdateOneFieldMetadataItem(
|
||||
$idToUpdate: ID!
|
||||
$updatePayload: UpdateFieldInput!
|
||||
) {
|
||||
updateOneField(input: { id: $idToUpdate, update: $updatePayload }) {
|
||||
id
|
||||
type
|
||||
name
|
||||
label
|
||||
description
|
||||
icon
|
||||
placeholder
|
||||
isCustom
|
||||
isActive
|
||||
isNullable
|
||||
createdAt
|
||||
updatedAt
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
export const UPDATE_ONE_METADATA_OBJECT = gql`
|
||||
mutation UpdateOneObjectMetadataItem(
|
||||
$idToUpdate: ID!
|
||||
$updatePayload: UpdateObjectInput!
|
||||
) {
|
||||
updateOneObject(input: { id: $idToUpdate, update: $updatePayload }) {
|
||||
id
|
||||
dataSourceId
|
||||
nameSingular
|
||||
namePlural
|
||||
labelSingular
|
||||
labelPlural
|
||||
description
|
||||
icon
|
||||
isCustom
|
||||
isActive
|
||||
createdAt
|
||||
updatedAt
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
export const DELETE_ONE_METADATA_OBJECT = gql`
|
||||
mutation DeleteOneObjectMetadataItem($idToDelete: ID!) {
|
||||
deleteOneObject(input: { id: $idToDelete }) {
|
||||
id
|
||||
dataSourceId
|
||||
nameSingular
|
||||
namePlural
|
||||
labelSingular
|
||||
labelPlural
|
||||
description
|
||||
icon
|
||||
isCustom
|
||||
isActive
|
||||
createdAt
|
||||
updatedAt
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
export const DELETE_ONE_METADATA_FIELD = gql`
|
||||
mutation DeleteOneFieldMetadataItem($idToDelete: ID!) {
|
||||
deleteOneField(input: { id: $idToDelete }) {
|
||||
id
|
||||
type
|
||||
name
|
||||
label
|
||||
description
|
||||
icon
|
||||
placeholder
|
||||
isCustom
|
||||
isActive
|
||||
isNullable
|
||||
createdAt
|
||||
updatedAt
|
||||
}
|
||||
}
|
||||
`;
|
||||
56
front/src/modules/object-metadata/graphql/queries.ts
Normal file
56
front/src/modules/object-metadata/graphql/queries.ts
Normal file
@ -0,0 +1,56 @@
|
||||
import { gql } from '@apollo/client';
|
||||
|
||||
export const FIND_MANY_METADATA_OBJECTS = gql`
|
||||
query ObjectMetadataItems {
|
||||
objects(paging: { first: 1000 }) {
|
||||
edges {
|
||||
node {
|
||||
id
|
||||
dataSourceId
|
||||
nameSingular
|
||||
namePlural
|
||||
labelSingular
|
||||
labelPlural
|
||||
description
|
||||
icon
|
||||
isCustom
|
||||
isActive
|
||||
createdAt
|
||||
updatedAt
|
||||
fields(paging: { first: 1000 }) {
|
||||
edges {
|
||||
node {
|
||||
id
|
||||
type
|
||||
name
|
||||
label
|
||||
description
|
||||
icon
|
||||
placeholder
|
||||
isCustom
|
||||
isActive
|
||||
isNullable
|
||||
createdAt
|
||||
updatedAt
|
||||
}
|
||||
}
|
||||
pageInfo {
|
||||
hasNextPage
|
||||
hasPreviousPage
|
||||
startCursor
|
||||
endCursor
|
||||
}
|
||||
totalCount
|
||||
}
|
||||
}
|
||||
}
|
||||
pageInfo {
|
||||
hasNextPage
|
||||
hasPreviousPage
|
||||
startCursor
|
||||
endCursor
|
||||
}
|
||||
totalCount
|
||||
}
|
||||
}
|
||||
`;
|
||||
@ -0,0 +1,9 @@
|
||||
import { useContext } from 'react';
|
||||
|
||||
import { ApolloMetadataClientContext } from '../context/ApolloClientMetadataContext';
|
||||
|
||||
export const useApolloMetadataClient = () => {
|
||||
const apolloMetadataClient = useContext(ApolloMetadataClientContext);
|
||||
|
||||
return apolloMetadataClient;
|
||||
};
|
||||
@ -0,0 +1,53 @@
|
||||
import { ApolloClient, useMutation } from '@apollo/client';
|
||||
import { getOperationName } from '@apollo/client/utilities';
|
||||
|
||||
import { FieldType } from '@/ui/object/field/types/FieldType';
|
||||
import { FieldMetadataType } from '~/generated/graphql';
|
||||
import {
|
||||
CreateOneFieldMetadataItemMutation,
|
||||
CreateOneFieldMetadataItemMutationVariables,
|
||||
} from '~/generated-metadata/graphql';
|
||||
|
||||
import { CREATE_ONE_METADATA_FIELD } from '../graphql/mutations';
|
||||
import { FIND_MANY_METADATA_OBJECTS } from '../graphql/queries';
|
||||
|
||||
import { useApolloMetadataClient } from './useApolloMetadataClient';
|
||||
|
||||
type CreateOneFieldMetadataItemArgs = Omit<
|
||||
CreateOneFieldMetadataItemMutationVariables['input']['field'],
|
||||
'type'
|
||||
> & {
|
||||
type: FieldType;
|
||||
};
|
||||
|
||||
export const useCreateOneFieldMetadataItem = () => {
|
||||
const apolloMetadataClient = useApolloMetadataClient();
|
||||
|
||||
const [mutate] = useMutation<
|
||||
CreateOneFieldMetadataItemMutation,
|
||||
CreateOneFieldMetadataItemMutationVariables
|
||||
>(CREATE_ONE_METADATA_FIELD, {
|
||||
client: apolloMetadataClient ?? ({} as ApolloClient<any>),
|
||||
});
|
||||
|
||||
const createOneFieldMetadataItem = async (
|
||||
input: CreateOneFieldMetadataItemArgs,
|
||||
) => {
|
||||
return await mutate({
|
||||
variables: {
|
||||
input: {
|
||||
field: {
|
||||
...input,
|
||||
type: input.type as FieldMetadataType, // Todo improve typing once we have aligned backend and frontend
|
||||
},
|
||||
},
|
||||
},
|
||||
awaitRefetchQueries: true,
|
||||
refetchQueries: [getOperationName(FIND_MANY_METADATA_OBJECTS) ?? ''],
|
||||
});
|
||||
};
|
||||
|
||||
return {
|
||||
createOneFieldMetadataItem,
|
||||
};
|
||||
};
|
||||
@ -0,0 +1,43 @@
|
||||
import { ApolloClient, useMutation } from '@apollo/client';
|
||||
import { getOperationName } from '@apollo/client/utilities';
|
||||
|
||||
import {
|
||||
CreateOneObjectMetadataItemMutation,
|
||||
CreateOneObjectMetadataItemMutationVariables,
|
||||
} from '~/generated-metadata/graphql';
|
||||
|
||||
import { CREATE_ONE_METADATA_OBJECT } from '../graphql/mutations';
|
||||
import { FIND_MANY_METADATA_OBJECTS } from '../graphql/queries';
|
||||
|
||||
import { useApolloMetadataClient } from './useApolloMetadataClient';
|
||||
|
||||
export const useCreateOneObjectRecordMetadataItem = () => {
|
||||
const apolloMetadataClient = useApolloMetadataClient();
|
||||
|
||||
const [mutate] = useMutation<
|
||||
CreateOneObjectMetadataItemMutation,
|
||||
CreateOneObjectMetadataItemMutationVariables
|
||||
>(CREATE_ONE_METADATA_OBJECT, {
|
||||
client: apolloMetadataClient ?? ({} as ApolloClient<any>),
|
||||
});
|
||||
|
||||
const createOneObjectMetadataItem = async (
|
||||
input: CreateOneObjectMetadataItemMutationVariables['input']['object'],
|
||||
) => {
|
||||
return await mutate({
|
||||
variables: {
|
||||
input: {
|
||||
object: {
|
||||
...input,
|
||||
},
|
||||
},
|
||||
},
|
||||
awaitRefetchQueries: true,
|
||||
refetchQueries: [getOperationName(FIND_MANY_METADATA_OBJECTS) ?? ''],
|
||||
});
|
||||
};
|
||||
|
||||
return {
|
||||
createOneObjectMetadataItem,
|
||||
};
|
||||
};
|
||||
@ -0,0 +1,39 @@
|
||||
import { ApolloClient, useMutation } from '@apollo/client';
|
||||
import { getOperationName } from '@apollo/client/utilities';
|
||||
|
||||
import {
|
||||
DeleteOneFieldMetadataItemMutation,
|
||||
DeleteOneFieldMetadataItemMutationVariables,
|
||||
} from '~/generated-metadata/graphql';
|
||||
|
||||
import { DELETE_ONE_METADATA_FIELD } from '../graphql/mutations';
|
||||
import { FIND_MANY_METADATA_OBJECTS } from '../graphql/queries';
|
||||
|
||||
import { useApolloMetadataClient } from './useApolloMetadataClient';
|
||||
|
||||
export const useDeleteOneFieldMetadataItem = () => {
|
||||
const apolloMetadataClient = useApolloMetadataClient();
|
||||
|
||||
const [mutate] = useMutation<
|
||||
DeleteOneFieldMetadataItemMutation,
|
||||
DeleteOneFieldMetadataItemMutationVariables
|
||||
>(DELETE_ONE_METADATA_FIELD, {
|
||||
client: apolloMetadataClient ?? ({} as ApolloClient<any>),
|
||||
});
|
||||
|
||||
const deleteOneFieldMetadataItem = async (
|
||||
idToDelete: DeleteOneFieldMetadataItemMutationVariables['idToDelete'],
|
||||
) => {
|
||||
return await mutate({
|
||||
variables: {
|
||||
idToDelete,
|
||||
},
|
||||
awaitRefetchQueries: true,
|
||||
refetchQueries: [getOperationName(FIND_MANY_METADATA_OBJECTS) ?? ''],
|
||||
});
|
||||
};
|
||||
|
||||
return {
|
||||
deleteOneFieldMetadataItem,
|
||||
};
|
||||
};
|
||||
@ -0,0 +1,39 @@
|
||||
import { ApolloClient, useMutation } from '@apollo/client';
|
||||
import { getOperationName } from '@apollo/client/utilities';
|
||||
|
||||
import {
|
||||
DeleteOneObjectMetadataItemMutation,
|
||||
DeleteOneObjectMetadataItemMutationVariables,
|
||||
} from '~/generated-metadata/graphql';
|
||||
|
||||
import { DELETE_ONE_METADATA_OBJECT } from '../graphql/mutations';
|
||||
import { FIND_MANY_METADATA_OBJECTS } from '../graphql/queries';
|
||||
|
||||
import { useApolloMetadataClient } from './useApolloMetadataClient';
|
||||
|
||||
export const useDeleteOneObjectMetadataItem = () => {
|
||||
const apolloMetadataClient = useApolloMetadataClient();
|
||||
|
||||
const [mutate] = useMutation<
|
||||
DeleteOneObjectMetadataItemMutation,
|
||||
DeleteOneObjectMetadataItemMutationVariables
|
||||
>(DELETE_ONE_METADATA_OBJECT, {
|
||||
client: apolloMetadataClient ?? ({} as ApolloClient<any>),
|
||||
});
|
||||
|
||||
const deleteOneObjectMetadataItem = async (
|
||||
idToDelete: DeleteOneObjectMetadataItemMutationVariables['idToDelete'],
|
||||
) => {
|
||||
return await mutate({
|
||||
variables: {
|
||||
idToDelete,
|
||||
},
|
||||
awaitRefetchQueries: true,
|
||||
refetchQueries: [getOperationName(FIND_MANY_METADATA_OBJECTS) ?? ''],
|
||||
});
|
||||
};
|
||||
|
||||
return {
|
||||
deleteOneObjectMetadataItem,
|
||||
};
|
||||
};
|
||||
@ -0,0 +1,57 @@
|
||||
import { MetadataFieldDataType } from '@/settings/data-model/types/ObjectFieldDataType';
|
||||
import { Field } from '~/generated/graphql';
|
||||
|
||||
import { formatFieldMetadataItemInput } from '../utils/formatFieldMetadataItemInput';
|
||||
|
||||
import { useCreateOneFieldMetadataItem } from './useCreateOneFieldMetadataItem';
|
||||
import { useDeleteOneFieldMetadataItem } from './useDeleteOneFieldMetadataItem';
|
||||
import { useUpdateOneFieldMetadataItem } from './useUpdateOneFieldMetadataItem';
|
||||
|
||||
export const useFieldMetadataItem = () => {
|
||||
const { createOneFieldMetadataItem } = useCreateOneFieldMetadataItem();
|
||||
const { updateOneFieldMetadataItem } = useUpdateOneFieldMetadataItem();
|
||||
const { deleteOneFieldMetadataItem } = useDeleteOneFieldMetadataItem();
|
||||
|
||||
const createMetadataField = (
|
||||
input: Pick<Field, 'label' | 'icon' | 'description'> & {
|
||||
objectMetadataId: string;
|
||||
type: MetadataFieldDataType;
|
||||
},
|
||||
) =>
|
||||
createOneFieldMetadataItem({
|
||||
...formatFieldMetadataItemInput(input),
|
||||
objectMetadataId: input.objectMetadataId,
|
||||
type: input.type,
|
||||
});
|
||||
|
||||
const editMetadataField = (
|
||||
input: Pick<Field, 'id' | 'label' | 'icon' | 'description'>,
|
||||
) =>
|
||||
updateOneFieldMetadataItem({
|
||||
fieldMetadataIdToUpdate: input.id,
|
||||
updatePayload: formatFieldMetadataItemInput(input),
|
||||
});
|
||||
|
||||
const activateMetadataField = (metadataField: Field) =>
|
||||
updateOneFieldMetadataItem({
|
||||
fieldMetadataIdToUpdate: metadataField.id,
|
||||
updatePayload: { isActive: true },
|
||||
});
|
||||
|
||||
const disableMetadataField = (metadataField: Field) =>
|
||||
updateOneFieldMetadataItem({
|
||||
fieldMetadataIdToUpdate: metadataField.id,
|
||||
updatePayload: { isActive: false },
|
||||
});
|
||||
|
||||
const eraseMetadataField = (metadataField: Field) =>
|
||||
deleteOneFieldMetadataItem(metadataField.id);
|
||||
|
||||
return {
|
||||
activateMetadataField,
|
||||
createMetadataField,
|
||||
disableMetadataField,
|
||||
eraseMetadataField,
|
||||
editMetadataField,
|
||||
};
|
||||
};
|
||||
@ -0,0 +1,69 @@
|
||||
import { useMemo } from 'react';
|
||||
import { useQuery } from '@apollo/client';
|
||||
|
||||
import { useSnackBar } from '@/ui/feedback/snack-bar/hooks/useSnackBar';
|
||||
import {
|
||||
ObjectMetadataItemsQuery,
|
||||
ObjectMetadataItemsQueryVariables,
|
||||
} from '~/generated-metadata/graphql';
|
||||
import { logError } from '~/utils/logError';
|
||||
|
||||
import { FIND_MANY_METADATA_OBJECTS } from '../graphql/queries';
|
||||
import { formatPagedObjectMetadataItemsToObjectMetadataItems } from '../utils/formatPagedObjectMetadataItemsToObjectMetadataItems';
|
||||
|
||||
import { useApolloMetadataClient } from './useApolloMetadataClient';
|
||||
|
||||
// TODO: test fetchMore
|
||||
export const useFindManyObjectMetadataItems = ({
|
||||
skip,
|
||||
}: { skip?: boolean } = {}) => {
|
||||
const apolloMetadataClient = useApolloMetadataClient();
|
||||
|
||||
const { enqueueSnackBar } = useSnackBar();
|
||||
|
||||
const {
|
||||
data,
|
||||
fetchMore: fetchMoreInternal,
|
||||
loading,
|
||||
error,
|
||||
} = useQuery<ObjectMetadataItemsQuery, ObjectMetadataItemsQueryVariables>(
|
||||
FIND_MANY_METADATA_OBJECTS,
|
||||
{
|
||||
client: apolloMetadataClient ?? undefined,
|
||||
skip: skip || !apolloMetadataClient,
|
||||
onError: (error) => {
|
||||
logError('useFindManyObjectMetadataItems error : ' + error);
|
||||
enqueueSnackBar(
|
||||
`Error during useFindManyObjectMetadataItems, ${error.message}`,
|
||||
{
|
||||
variant: 'error',
|
||||
},
|
||||
);
|
||||
},
|
||||
onCompleted: () => {},
|
||||
},
|
||||
);
|
||||
|
||||
const hasMore = data?.objects?.pageInfo?.hasNextPage;
|
||||
|
||||
const fetchMore = () =>
|
||||
fetchMoreInternal({
|
||||
variables: {
|
||||
afterCursor: data?.objects?.pageInfo?.endCursor,
|
||||
},
|
||||
});
|
||||
|
||||
const objectMetadataItems = useMemo(() => {
|
||||
return formatPagedObjectMetadataItemsToObjectMetadataItems({
|
||||
pagedObjectMetadataItems: data,
|
||||
});
|
||||
}, [data]);
|
||||
|
||||
return {
|
||||
objectMetadataItems,
|
||||
hasMore,
|
||||
fetchMore,
|
||||
loading,
|
||||
error,
|
||||
};
|
||||
};
|
||||
@ -0,0 +1,126 @@
|
||||
import { gql } from '@apollo/client';
|
||||
|
||||
import { generateCreateOneObjectMutation } from '@/object-record/utils/generateCreateOneObjectMutation';
|
||||
import { generateDeleteOneObjectMutation } from '@/object-record/utils/generateDeleteOneObjectMutation';
|
||||
import { generateFindManyCustomObjectsQuery } from '@/object-record/utils/generateFindManyCustomObjectsQuery';
|
||||
import { generateFindOneCustomObjectQuery } from '@/object-record/utils/generateFindOneCustomObjectQuery';
|
||||
import { generateUpdateOneObjectMutation } from '@/object-record/utils/generateUpdateOneObjectMutation';
|
||||
import { useLazyLoadIcons } from '@/ui/input/hooks/useLazyLoadIcons';
|
||||
import { FieldMetadata } from '@/ui/object/field/types/FieldMetadata';
|
||||
import { FilterDefinition } from '@/ui/object/object-filter-dropdown/types/FilterDefinition';
|
||||
import { SortDefinition } from '@/ui/object/object-sort-dropdown/types/SortDefinition';
|
||||
import { ColumnDefinition } from '@/ui/object/record-table/types/ColumnDefinition';
|
||||
|
||||
import { ObjectMetadataItemIdentifier } from '../types/ObjectMetadataItemIdentifier';
|
||||
import { formatFieldMetadataItemAsColumnDefinition } from '../utils/formatFieldMetadataItemAsColumnDefinition';
|
||||
import { formatFieldMetadataItemAsFilterDefinition } from '../utils/formatFieldMetadataItemAsFilterDefinition';
|
||||
import { formatFieldMetadataItemAsSortDefinition } from '../utils/formatFieldMetadataItemAsSortDefinition';
|
||||
|
||||
import { useFindManyObjectMetadataItems } from './useFindManyObjectMetadataItems';
|
||||
|
||||
const EMPTY_QUERY = gql`
|
||||
query EmptyQuery {
|
||||
empty
|
||||
}
|
||||
`;
|
||||
|
||||
const EMPTY_MUTATION = gql`
|
||||
mutation EmptyMutation {
|
||||
empty
|
||||
}
|
||||
`;
|
||||
|
||||
export const useFindOneObjectMetadataItem = ({
|
||||
objectNamePlural,
|
||||
objectNameSingular,
|
||||
skip,
|
||||
}: ObjectMetadataItemIdentifier & { skip?: boolean }) => {
|
||||
const { objectMetadataItems, loading } = useFindManyObjectMetadataItems({
|
||||
skip,
|
||||
});
|
||||
|
||||
const foundObjectMetadataItem = objectMetadataItems.find(
|
||||
(object) =>
|
||||
object.namePlural === objectNamePlural ||
|
||||
object.nameSingular === objectNameSingular,
|
||||
);
|
||||
|
||||
const { icons } = useLazyLoadIcons();
|
||||
|
||||
const objectNotFoundInMetadata =
|
||||
objectMetadataItems.length === 0 ||
|
||||
(objectMetadataItems.length > 0 && !foundObjectMetadataItem);
|
||||
|
||||
const activeFields =
|
||||
foundObjectMetadataItem?.fields.filter(({ isActive }) => isActive) ?? [];
|
||||
|
||||
const columnDefinitions: ColumnDefinition<FieldMetadata>[] =
|
||||
foundObjectMetadataItem
|
||||
? activeFields.map((field, index) =>
|
||||
formatFieldMetadataItemAsColumnDefinition({
|
||||
position: index,
|
||||
field,
|
||||
objectMetadataItem: foundObjectMetadataItem,
|
||||
icons,
|
||||
}),
|
||||
)
|
||||
: [];
|
||||
|
||||
const filterDefinitions: FilterDefinition[] = activeFields.map((field) =>
|
||||
formatFieldMetadataItemAsFilterDefinition({
|
||||
field,
|
||||
icons,
|
||||
}),
|
||||
);
|
||||
|
||||
const sortDefinitions: SortDefinition[] = activeFields.map((field) =>
|
||||
formatFieldMetadataItemAsSortDefinition({
|
||||
field,
|
||||
icons,
|
||||
}),
|
||||
);
|
||||
|
||||
const findManyQuery = foundObjectMetadataItem
|
||||
? generateFindManyCustomObjectsQuery({
|
||||
objectMetadataItem: foundObjectMetadataItem,
|
||||
})
|
||||
: EMPTY_QUERY;
|
||||
|
||||
const findOneQuery = foundObjectMetadataItem
|
||||
? generateFindOneCustomObjectQuery({
|
||||
objectMetadataItem: foundObjectMetadataItem,
|
||||
})
|
||||
: EMPTY_QUERY;
|
||||
|
||||
const createOneMutation = foundObjectMetadataItem
|
||||
? generateCreateOneObjectMutation({
|
||||
objectMetadataItem: foundObjectMetadataItem,
|
||||
})
|
||||
: EMPTY_MUTATION;
|
||||
|
||||
const updateOneMutation = foundObjectMetadataItem
|
||||
? generateUpdateOneObjectMutation({
|
||||
objectMetadataItem: foundObjectMetadataItem,
|
||||
})
|
||||
: EMPTY_MUTATION;
|
||||
|
||||
const deleteOneMutation = foundObjectMetadataItem
|
||||
? generateDeleteOneObjectMutation({
|
||||
objectMetadataItem: foundObjectMetadataItem,
|
||||
})
|
||||
: EMPTY_MUTATION;
|
||||
|
||||
return {
|
||||
foundObjectMetadataItem,
|
||||
objectNotFoundInMetadata,
|
||||
columnDefinitions,
|
||||
filterDefinitions,
|
||||
sortDefinitions,
|
||||
findManyQuery,
|
||||
findOneQuery,
|
||||
createOneMutation,
|
||||
updateOneMutation,
|
||||
deleteOneMutation,
|
||||
loading,
|
||||
};
|
||||
};
|
||||
@ -0,0 +1,80 @@
|
||||
import { ObjectMetadataItem } from '../types/ObjectMetadataItem';
|
||||
import { formatObjectMetadataItemInput } from '../utils/formatObjectMetadataItemInput';
|
||||
import { getObjectSlug } from '../utils/getObjectSlug';
|
||||
|
||||
import { useCreateOneObjectRecordMetadataItem } from './useCreateOneObjectMetadataItem';
|
||||
import { useDeleteOneObjectMetadataItem } from './useDeleteOneObjectMetadataItem';
|
||||
import { useFindManyObjectMetadataItems } from './useFindManyObjectMetadataItems';
|
||||
import { useUpdateOneObjectMetadataItem } from './useUpdateOneObjectMetadataItem';
|
||||
|
||||
export const useObjectMetadataItemForSettings = () => {
|
||||
const { objectMetadataItems, loading } = useFindManyObjectMetadataItems();
|
||||
|
||||
const activeObjectMetadataItems = objectMetadataItems.filter(
|
||||
({ isActive }) => isActive,
|
||||
);
|
||||
const disabledObjectMetadataItems = objectMetadataItems.filter(
|
||||
({ isActive }) => !isActive,
|
||||
);
|
||||
|
||||
const findActiveObjectMetadataItemBySlug = (slug: string) =>
|
||||
activeObjectMetadataItems.find(
|
||||
(activeObjectMetadataItem) =>
|
||||
getObjectSlug(activeObjectMetadataItem) === slug,
|
||||
);
|
||||
|
||||
const { createOneObjectMetadataItem } =
|
||||
useCreateOneObjectRecordMetadataItem();
|
||||
const { updateOneObjectMetadataItem } = useUpdateOneObjectMetadataItem();
|
||||
const { deleteOneObjectMetadataItem } = useDeleteOneObjectMetadataItem();
|
||||
|
||||
const createObjectMetadataItem = (
|
||||
input: Pick<
|
||||
ObjectMetadataItem,
|
||||
'labelPlural' | 'labelSingular' | 'icon' | 'description'
|
||||
>,
|
||||
) => createOneObjectMetadataItem(formatObjectMetadataItemInput(input));
|
||||
|
||||
const editObjectMetadataItem = (
|
||||
input: Pick<
|
||||
ObjectMetadataItem,
|
||||
'id' | 'labelPlural' | 'labelSingular' | 'icon' | 'description'
|
||||
>,
|
||||
) =>
|
||||
updateOneObjectMetadataItem({
|
||||
idToUpdate: input.id,
|
||||
updatePayload: formatObjectMetadataItemInput(input),
|
||||
});
|
||||
|
||||
const activateObjectMetadataItem = (
|
||||
objectMetadataItem: Pick<ObjectMetadataItem, 'id'>,
|
||||
) =>
|
||||
updateOneObjectMetadataItem({
|
||||
idToUpdate: objectMetadataItem.id,
|
||||
updatePayload: { isActive: true },
|
||||
});
|
||||
|
||||
const disableObjectMetadataItem = (
|
||||
objectMetadataItem: Pick<ObjectMetadataItem, 'id'>,
|
||||
) =>
|
||||
updateOneObjectMetadataItem({
|
||||
idToUpdate: objectMetadataItem.id,
|
||||
updatePayload: { isActive: false },
|
||||
});
|
||||
|
||||
const eraseObjectMetadataItem = (
|
||||
objectMetadataItem: Pick<ObjectMetadataItem, 'id'>,
|
||||
) => deleteOneObjectMetadataItem(objectMetadataItem.id);
|
||||
|
||||
return {
|
||||
activateObjectMetadataItem,
|
||||
activeObjectMetadataItems,
|
||||
createObjectMetadataItem,
|
||||
disabledObjectMetadataItems,
|
||||
disableObjectMetadataItem,
|
||||
editObjectMetadataItem,
|
||||
eraseObjectMetadataItem,
|
||||
findActiveObjectMetadataItemBySlug,
|
||||
loading,
|
||||
};
|
||||
};
|
||||
@ -0,0 +1,50 @@
|
||||
import { useMutation } from '@apollo/client';
|
||||
import { getOperationName } from '@apollo/client/utilities';
|
||||
|
||||
import {
|
||||
UpdateOneFieldMetadataItemMutation,
|
||||
UpdateOneFieldMetadataItemMutationVariables,
|
||||
} from '~/generated-metadata/graphql';
|
||||
|
||||
import { UPDATE_ONE_METADATA_FIELD } from '../graphql/mutations';
|
||||
import { FIND_MANY_METADATA_OBJECTS } from '../graphql/queries';
|
||||
|
||||
import { useApolloMetadataClient } from './useApolloMetadataClient';
|
||||
|
||||
export const useUpdateOneFieldMetadataItem = () => {
|
||||
const apolloMetadataClient = useApolloMetadataClient();
|
||||
|
||||
const [mutate] = useMutation<
|
||||
UpdateOneFieldMetadataItemMutation,
|
||||
UpdateOneFieldMetadataItemMutationVariables
|
||||
>(UPDATE_ONE_METADATA_FIELD, {
|
||||
client: apolloMetadataClient ?? undefined,
|
||||
});
|
||||
|
||||
const updateOneFieldMetadataItem = async ({
|
||||
fieldMetadataIdToUpdate,
|
||||
updatePayload,
|
||||
}: {
|
||||
fieldMetadataIdToUpdate: UpdateOneFieldMetadataItemMutationVariables['idToUpdate'];
|
||||
updatePayload: Pick<
|
||||
UpdateOneFieldMetadataItemMutationVariables['updatePayload'],
|
||||
'description' | 'icon' | 'isActive' | 'label' | 'name'
|
||||
>;
|
||||
}) => {
|
||||
return await mutate({
|
||||
variables: {
|
||||
idToUpdate: fieldMetadataIdToUpdate,
|
||||
updatePayload: {
|
||||
...updatePayload,
|
||||
label: updatePayload.label ?? undefined,
|
||||
},
|
||||
},
|
||||
awaitRefetchQueries: true,
|
||||
refetchQueries: [getOperationName(FIND_MANY_METADATA_OBJECTS) ?? ''],
|
||||
});
|
||||
};
|
||||
|
||||
return {
|
||||
updateOneFieldMetadataItem,
|
||||
};
|
||||
};
|
||||
@ -0,0 +1,54 @@
|
||||
import { useMutation } from '@apollo/client';
|
||||
import { getOperationName } from '@apollo/client/utilities';
|
||||
|
||||
import {
|
||||
UpdateOneObjectMetadataItemMutation,
|
||||
UpdateOneObjectMetadataItemMutationVariables,
|
||||
} from '~/generated-metadata/graphql';
|
||||
|
||||
import { UPDATE_ONE_METADATA_OBJECT } from '../graphql/mutations';
|
||||
import { FIND_MANY_METADATA_OBJECTS } from '../graphql/queries';
|
||||
|
||||
import { useApolloMetadataClient } from './useApolloMetadataClient';
|
||||
|
||||
// TODO: Slice the Apollo store synchronously in the update function instead of subscribing, so we can use update after read in the same function call
|
||||
export const useUpdateOneObjectMetadataItem = () => {
|
||||
const apolloClientMetadata = useApolloMetadataClient();
|
||||
|
||||
const [mutate] = useMutation<
|
||||
UpdateOneObjectMetadataItemMutation,
|
||||
UpdateOneObjectMetadataItemMutationVariables
|
||||
>(UPDATE_ONE_METADATA_OBJECT, {
|
||||
client: apolloClientMetadata ?? undefined,
|
||||
});
|
||||
|
||||
const updateOneObjectMetadataItem = async ({
|
||||
idToUpdate,
|
||||
updatePayload,
|
||||
}: {
|
||||
idToUpdate: UpdateOneObjectMetadataItemMutationVariables['idToUpdate'];
|
||||
updatePayload: Pick<
|
||||
UpdateOneObjectMetadataItemMutationVariables['updatePayload'],
|
||||
| 'description'
|
||||
| 'icon'
|
||||
| 'isActive'
|
||||
| 'labelPlural'
|
||||
| 'labelSingular'
|
||||
| 'namePlural'
|
||||
| 'nameSingular'
|
||||
>;
|
||||
}) => {
|
||||
return await mutate({
|
||||
variables: {
|
||||
idToUpdate,
|
||||
updatePayload,
|
||||
},
|
||||
awaitRefetchQueries: true,
|
||||
refetchQueries: [getOperationName(FIND_MANY_METADATA_OBJECTS) ?? ''],
|
||||
});
|
||||
};
|
||||
|
||||
return {
|
||||
updateOneObjectMetadataItem,
|
||||
};
|
||||
};
|
||||
@ -0,0 +1,11 @@
|
||||
import { atom } from 'recoil';
|
||||
|
||||
import { ObjectMetadataItem } from '../types/ObjectMetadataItem';
|
||||
|
||||
/**
|
||||
* @deprecated Use `useFindManyObjectMetadataItems` instead.
|
||||
*/
|
||||
export const objectMetadataItemsState = atom<ObjectMetadataItem[]>({
|
||||
key: 'objectMetadataItemsState',
|
||||
default: [],
|
||||
});
|
||||
@ -0,0 +1,12 @@
|
||||
import { selector } from 'recoil';
|
||||
|
||||
import { ObjectMetadataItem } from '../../types/ObjectMetadataItem';
|
||||
import { objectMetadataItemsState } from '../objectMetadataItemsState';
|
||||
|
||||
export const activeObjectMetadataItemsSelector = selector<ObjectMetadataItem[]>(
|
||||
{
|
||||
key: 'activeObjectMetadataItemsSelector',
|
||||
get: ({ get }) =>
|
||||
get(objectMetadataItemsState).filter(({ isActive }) => isActive),
|
||||
},
|
||||
);
|
||||
@ -0,0 +1,12 @@
|
||||
import { selector } from 'recoil';
|
||||
|
||||
import { ObjectMetadataItem } from '../../types/ObjectMetadataItem';
|
||||
import { objectMetadataItemsState } from '../objectMetadataItemsState';
|
||||
|
||||
export const disabledObjectMetadataItemsSelector = selector<
|
||||
ObjectMetadataItem[]
|
||||
>({
|
||||
key: 'disabledObjectMetadataItemsSelector',
|
||||
get: ({ get }) =>
|
||||
get(objectMetadataItemsState).filter(({ isActive }) => !isActive),
|
||||
});
|
||||
@ -0,0 +1,8 @@
|
||||
import { Field, Object as GeneratedObject } from '~/generated-metadata/graphql';
|
||||
|
||||
export type ObjectMetadataItem = Omit<
|
||||
GeneratedObject,
|
||||
'fields' | 'dataSourceId'
|
||||
> & {
|
||||
fields: Field[];
|
||||
};
|
||||
@ -0,0 +1,4 @@
|
||||
export type ObjectMetadataItemIdentifier = {
|
||||
objectNamePlural?: string;
|
||||
objectNameSingular?: string;
|
||||
};
|
||||
@ -0,0 +1,33 @@
|
||||
import { IconComponent } from '@/ui/display/icon/types/IconComponent';
|
||||
import { FieldMetadata } from '@/ui/object/field/types/FieldMetadata';
|
||||
import { ColumnDefinition } from '@/ui/object/record-table/types/ColumnDefinition';
|
||||
import { Field } from '~/generated-metadata/graphql';
|
||||
|
||||
import { ObjectMetadataItem } from '../types/ObjectMetadataItem';
|
||||
|
||||
import { parseFieldType } from './parseFieldType';
|
||||
|
||||
export const formatFieldMetadataItemAsColumnDefinition = ({
|
||||
position,
|
||||
field,
|
||||
objectMetadataItem,
|
||||
icons,
|
||||
}: {
|
||||
position: number;
|
||||
field: Field;
|
||||
objectMetadataItem: Omit<ObjectMetadataItem, 'fields'>;
|
||||
icons: Record<string, IconComponent>;
|
||||
}): ColumnDefinition<FieldMetadata> => ({
|
||||
position,
|
||||
fieldMetadataId: field.id,
|
||||
label: field.label,
|
||||
size: 100,
|
||||
type: parseFieldType(field.type),
|
||||
metadata: {
|
||||
fieldName: field.name,
|
||||
placeHolder: field.label,
|
||||
},
|
||||
Icon: icons[field.icon ?? 'Icon123'],
|
||||
isVisible: true,
|
||||
basePathToShowPage: `/object/${objectMetadataItem.nameSingular}/`,
|
||||
});
|
||||
@ -0,0 +1,16 @@
|
||||
import { FilterDefinition } from '@/ui/object/object-filter-dropdown/types/FilterDefinition';
|
||||
|
||||
import { ObjectMetadataItem } from '../types/ObjectMetadataItem';
|
||||
|
||||
export const formatFieldMetadataItemAsFilterDefinition = ({
|
||||
field,
|
||||
icons,
|
||||
}: {
|
||||
field: ObjectMetadataItem['fields'][0];
|
||||
icons: Record<string, any>;
|
||||
}): FilterDefinition => ({
|
||||
fieldMetadataId: field.id,
|
||||
label: field.label,
|
||||
Icon: icons[field.icon ?? 'Icon123'],
|
||||
type: 'TEXT',
|
||||
});
|
||||
@ -0,0 +1,15 @@
|
||||
import { SortDefinition } from '@/ui/object/object-sort-dropdown/types/SortDefinition';
|
||||
|
||||
import { ObjectMetadataItem } from '../types/ObjectMetadataItem';
|
||||
|
||||
export const formatFieldMetadataItemAsSortDefinition = ({
|
||||
field,
|
||||
icons,
|
||||
}: {
|
||||
field: ObjectMetadataItem['fields'][0];
|
||||
icons: Record<string, any>;
|
||||
}): SortDefinition => ({
|
||||
fieldMetadataId: field.id,
|
||||
label: field.label,
|
||||
Icon: icons[field.icon ?? 'Icon123'],
|
||||
});
|
||||
@ -0,0 +1,12 @@
|
||||
import toCamelCase from 'lodash.camelcase';
|
||||
|
||||
import { Field } from '~/generated-metadata/graphql';
|
||||
|
||||
export const formatFieldMetadataItemInput = (
|
||||
input: Pick<Field, 'label' | 'icon' | 'description'>,
|
||||
) => ({
|
||||
description: input.description?.trim() ?? null,
|
||||
icon: input.icon,
|
||||
label: input.label.trim(),
|
||||
name: toCamelCase(input.label.trim()),
|
||||
});
|
||||
@ -0,0 +1,17 @@
|
||||
import toCamelCase from 'lodash.camelcase';
|
||||
|
||||
import { ObjectMetadataItem } from '../types/ObjectMetadataItem';
|
||||
|
||||
export const formatObjectMetadataItemInput = (
|
||||
input: Pick<
|
||||
ObjectMetadataItem,
|
||||
'labelPlural' | 'labelSingular' | 'icon' | 'description'
|
||||
>,
|
||||
) => ({
|
||||
description: input.description?.trim() ?? null,
|
||||
icon: input.icon,
|
||||
labelPlural: input.labelPlural.trim(),
|
||||
labelSingular: input.labelSingular.trim(),
|
||||
namePlural: toCamelCase(input.labelPlural.trim()),
|
||||
nameSingular: toCamelCase(input.labelSingular.trim()),
|
||||
});
|
||||
@ -0,0 +1,17 @@
|
||||
import { ObjectMetadataItemsQuery } from '~/generated-metadata/graphql';
|
||||
|
||||
import { ObjectMetadataItem } from '../types/ObjectMetadataItem';
|
||||
|
||||
export const formatPagedObjectMetadataItemsToObjectMetadataItems = ({
|
||||
pagedObjectMetadataItems: pagedObjectMetadataItems,
|
||||
}: {
|
||||
pagedObjectMetadataItems: ObjectMetadataItemsQuery | undefined;
|
||||
}) => {
|
||||
const formattedObjects: ObjectMetadataItem[] =
|
||||
pagedObjectMetadataItems?.objects.edges.map((object) => ({
|
||||
...object.node,
|
||||
fields: object.node.fields.edges.map((field) => field.node),
|
||||
})) ?? [];
|
||||
|
||||
return formattedObjects;
|
||||
};
|
||||
6
front/src/modules/object-metadata/utils/getFieldSlug.ts
Normal file
6
front/src/modules/object-metadata/utils/getFieldSlug.ts
Normal file
@ -0,0 +1,6 @@
|
||||
import toKebabCase from 'lodash.kebabcase';
|
||||
|
||||
import { Field } from '~/generated-metadata/graphql';
|
||||
|
||||
export const getFieldSlug = (metadataField: Pick<Field, 'label'>) =>
|
||||
toKebabCase(metadataField.label);
|
||||
7
front/src/modules/object-metadata/utils/getObjectSlug.ts
Normal file
7
front/src/modules/object-metadata/utils/getObjectSlug.ts
Normal file
@ -0,0 +1,7 @@
|
||||
import toKebabCase from 'lodash.kebabcase';
|
||||
|
||||
import { ObjectMetadataItem } from '../types/ObjectMetadataItem';
|
||||
|
||||
export const getObjectSlug = (
|
||||
objectMetadataItem: Pick<ObjectMetadataItem, 'labelPlural'>,
|
||||
) => toKebabCase(objectMetadataItem.labelPlural);
|
||||
@ -0,0 +1,44 @@
|
||||
import { FieldType } from '@/ui/object/field/types/FieldType';
|
||||
import { Field } from '~/generated/graphql';
|
||||
|
||||
export const mapFieldMetadataToGraphQLQuery = (field: Field) => {
|
||||
// TODO: parse
|
||||
const fieldType = field.type as FieldType;
|
||||
|
||||
const fieldIsSimpleValue = (
|
||||
[
|
||||
'TEXT',
|
||||
'PHONE',
|
||||
'DATE',
|
||||
'EMAIL',
|
||||
'NUMBER',
|
||||
'BOOLEAN',
|
||||
'DATE',
|
||||
] as FieldType[]
|
||||
).includes(fieldType);
|
||||
|
||||
const fieldIsURL = fieldType === 'URL' || fieldType === 'URL_V2';
|
||||
|
||||
const fieldIsMoneyAmount =
|
||||
fieldType === 'MONEY' || fieldType === 'MONEY_AMOUNT_V2';
|
||||
|
||||
if (fieldIsSimpleValue) {
|
||||
return field.name;
|
||||
} else if (fieldIsURL) {
|
||||
return `
|
||||
${field.name}
|
||||
{
|
||||
text
|
||||
link
|
||||
}
|
||||
`;
|
||||
} else if (fieldIsMoneyAmount) {
|
||||
return `
|
||||
${field.name}
|
||||
{
|
||||
amount
|
||||
currency
|
||||
}
|
||||
`;
|
||||
}
|
||||
};
|
||||
14
front/src/modules/object-metadata/utils/parseFieldType.ts
Normal file
14
front/src/modules/object-metadata/utils/parseFieldType.ts
Normal file
@ -0,0 +1,14 @@
|
||||
import { FieldType } from '@/ui/object/field/types/FieldType';
|
||||
import { FieldMetadataType } from '~/generated-metadata/graphql';
|
||||
|
||||
export const parseFieldType = (fieldType: FieldMetadataType): FieldType => {
|
||||
if (fieldType === FieldMetadataType.Url) {
|
||||
return 'URL_V2';
|
||||
}
|
||||
|
||||
if (fieldType === FieldMetadataType.Money) {
|
||||
return 'MONEY_AMOUNT_V2';
|
||||
}
|
||||
|
||||
return fieldType as FieldType;
|
||||
};
|
||||
@ -0,0 +1,4 @@
|
||||
const metadataLabelValidationPattern = /^[a-zA-Z][a-zA-Z0-9 ]*$/;
|
||||
|
||||
export const validateMetadataLabel = (value: string) =>
|
||||
!!value.match(metadataLabelValidationPattern);
|
||||
Reference in New Issue
Block a user