Core views frontend (#13932)
Parallel code path to read and write core views when IS_CORE_VIEW_ENABLED. Migrated view key to an enum. --------- Co-authored-by: Charles Bochet <charles@twenty.com> Co-authored-by: Lucas Bordeau <bordeau.lucas@gmail.com>
This commit is contained in:
@@ -7,6 +7,7 @@ module.exports = {
|
||||
documents: [
|
||||
'./src/modules/auth/graphql/**/*.{ts,tsx}',
|
||||
'./src/modules/users/graphql/**/*.{ts,tsx}',
|
||||
'./src/modules/views/graphql/**/*.{ts,tsx}',
|
||||
'./src/modules/ai/graphql/**/*.{ts,tsx}',
|
||||
|
||||
'./src/modules/workspace/graphql/**/*.{ts,tsx}',
|
||||
|
||||
@@ -61,9 +61,9 @@ const jestConfig = {
|
||||
extensionsToTreatAsEsm: ['.ts', '.tsx'],
|
||||
coverageThreshold: {
|
||||
global: {
|
||||
statements: 54,
|
||||
statements: 53,
|
||||
lines: 53,
|
||||
functions: 43,
|
||||
functions: 42,
|
||||
},
|
||||
},
|
||||
collectCoverageFrom: ['<rootDir>/src/**/*.ts'],
|
||||
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large
Load Diff
@@ -14,7 +14,6 @@ import { clientConfigApiStatusState } from '@/client-config/states/clientConfigA
|
||||
import { supportChatState } from '@/client-config/states/supportChatState';
|
||||
import { REACT_APP_SERVER_BASE_URL } from '~/config';
|
||||
import {
|
||||
type AuthTokenPair,
|
||||
useCheckUserExistsLazyQuery,
|
||||
useGetAuthTokensFromLoginTokenMutation,
|
||||
useGetAuthTokensFromOtpMutation,
|
||||
@@ -24,6 +23,7 @@ import {
|
||||
useSignInMutation,
|
||||
useSignUpInWorkspaceMutation,
|
||||
useSignUpMutation,
|
||||
type AuthTokenPair,
|
||||
} from '~/generated-metadata/graphql';
|
||||
|
||||
import { isDeveloperDefaultSignInPrefilledState } from '@/client-config/states/isDeveloperDefaultSignInPrefilledState';
|
||||
|
||||
+19
-11
@@ -14,6 +14,8 @@ import { SettingsPath } from '@/types/SettingsPath';
|
||||
import { TableCell } from '@/ui/layout/table/components/TableCell';
|
||||
import { TableRow } from '@/ui/layout/table/components/TableRow';
|
||||
import { navigationMemorizedUrlState } from '@/ui/navigation/states/navigationMemorizedUrlState';
|
||||
import { type View } from '@/views/types/View';
|
||||
import { useFeatureFlagsMap } from '@/workspace/hooks/useFeatureFlagsMap';
|
||||
import { useTheme } from '@emotion/react';
|
||||
import styled from '@emotion/styled';
|
||||
import { useMemo } from 'react';
|
||||
@@ -25,7 +27,7 @@ import {
|
||||
import { IconMinus, IconPlus, useIcons } from 'twenty-ui/display';
|
||||
import { LightIconButton } from 'twenty-ui/input';
|
||||
import { UndecoratedLink } from 'twenty-ui/navigation';
|
||||
import { RelationType } from '~/generated-metadata/graphql';
|
||||
import { FeatureFlagKey, RelationType } from '~/generated-metadata/graphql';
|
||||
import { useNavigateSettings } from '~/hooks/useNavigateSettings';
|
||||
import { type SettingsObjectDetailTableItem } from '~/pages/settings/data-model/types/SettingsObjectDetailTableItem';
|
||||
import { getSettingsPath } from '~/utils/navigation/getSettingsPath';
|
||||
@@ -122,6 +124,9 @@ export const SettingsObjectFieldItemTableRow = ({
|
||||
objectNameSingular: CoreObjectNameSingular.View,
|
||||
});
|
||||
|
||||
const featureFlagMap = useFeatureFlagsMap();
|
||||
const isCoreViewEnabled = featureFlagMap[FeatureFlagKey.IS_CORE_VIEW_ENABLED];
|
||||
|
||||
const handleDisableField = async (
|
||||
activeFieldMetadatItem: FieldMetadataItem,
|
||||
) => {
|
||||
@@ -130,17 +135,20 @@ export const SettingsObjectFieldItemTableRow = ({
|
||||
objectMetadataItem.id,
|
||||
);
|
||||
|
||||
const deletedViewIds = prefetchViews
|
||||
.map((view) => {
|
||||
// TODO: replace with viewGroups.fieldMetadataId
|
||||
if (view.kanbanFieldMetadataId === activeFieldMetadatItem.id) {
|
||||
deleteViewFromCache(view);
|
||||
return view.id;
|
||||
}
|
||||
// TODO: Add optimistic rendering for core views
|
||||
const deletedViewIds = isCoreViewEnabled
|
||||
? []
|
||||
: (prefetchViews as View[])
|
||||
.map((view) => {
|
||||
// TODO: replace with viewGroups.fieldMetadataId
|
||||
if (view.kanbanFieldMetadataId === activeFieldMetadatItem.id) {
|
||||
deleteViewFromCache(view);
|
||||
return view.id;
|
||||
}
|
||||
|
||||
return null;
|
||||
})
|
||||
.filter(isDefined);
|
||||
return null;
|
||||
})
|
||||
.filter(isDefined);
|
||||
|
||||
const [baseUrl, queryParams] = navigationMemorizedUrl.includes('?')
|
||||
? navigationMemorizedUrl.split('?')
|
||||
|
||||
@@ -5,6 +5,12 @@ import {
|
||||
import { OBJECT_PERMISSION_FRAGMENT } from '@/settings/roles/graphql/fragments/objectPermissionFragment';
|
||||
import { ROLE_FRAGMENT } from '@/settings/roles/graphql/fragments/roleFragment';
|
||||
import { WORKSPACE_URLS_FRAGMENT } from '@/users/graphql/fragments/workspaceUrlsFragment';
|
||||
import { VIEW_FIELD_FRAGMENT } from '@/views/graphql/fragments/viewFieldFragment';
|
||||
import { VIEW_FILTER_FRAGMENT } from '@/views/graphql/fragments/viewFilterFragment';
|
||||
import { VIEW_FILTER_GROUP_FRAGMENT } from '@/views/graphql/fragments/viewFilterGroupFragment';
|
||||
import { VIEW_FRAGMENT } from '@/views/graphql/fragments/viewFragment';
|
||||
import { VIEW_GROUP_FRAGMENT } from '@/views/graphql/fragments/viewGroupFragment';
|
||||
import { VIEW_SORT_FRAGMENT } from '@/views/graphql/fragments/viewSortFragment';
|
||||
import { DELETED_WORKSPACE_MEMBER_QUERY_FRAGMENT } from '@/workspace-member/graphql/fragments/deletedWorkspaceMemberQueryFragment';
|
||||
import { WORKSPACE_MEMBER_QUERY_FRAGMENT } from '@/workspace-member/graphql/fragments/workspaceMemberQueryFragment';
|
||||
import { gql } from '@apollo/client';
|
||||
@@ -96,6 +102,24 @@ export const USER_QUERY_FRAGMENT = gql`
|
||||
id
|
||||
}
|
||||
isTwoFactorAuthenticationEnforced
|
||||
views {
|
||||
...ViewFragment
|
||||
viewFields {
|
||||
...ViewFieldFragment
|
||||
}
|
||||
viewFilters {
|
||||
...ViewFilterFragment
|
||||
}
|
||||
viewFilterGroups {
|
||||
...ViewFilterGroupFragment
|
||||
}
|
||||
viewSorts {
|
||||
...ViewSortFragment
|
||||
}
|
||||
viewGroups {
|
||||
...ViewGroupFragment
|
||||
}
|
||||
}
|
||||
}
|
||||
availableWorkspaces {
|
||||
...AvailableWorkspacesFragment
|
||||
@@ -108,6 +132,12 @@ export const USER_QUERY_FRAGMENT = gql`
|
||||
${OBJECT_PERMISSION_FRAGMENT}
|
||||
${WORKSPACE_URLS_FRAGMENT}
|
||||
${ROLE_FRAGMENT}
|
||||
${VIEW_FRAGMENT}
|
||||
${VIEW_FIELD_FRAGMENT}
|
||||
${VIEW_FILTER_FRAGMENT}
|
||||
${VIEW_FILTER_GROUP_FRAGMENT}
|
||||
${VIEW_SORT_FRAGMENT}
|
||||
${VIEW_GROUP_FRAGMENT}
|
||||
${AVAILABLE_WORKSPACES_FOR_AUTH_FRAGMENT}
|
||||
${AVAILABLE_WORKSPACE_FOR_AUTH_FRAGMENT}
|
||||
`;
|
||||
|
||||
@@ -16,7 +16,7 @@ import { getDateFormatFromWorkspaceDateFormat } from '@/localization/utils/getDa
|
||||
import { getTimeFormatFromWorkspaceTimeFormat } from '@/localization/utils/getTimeFormatFromWorkspaceTimeFormat';
|
||||
import { useCallback } from 'react';
|
||||
import { useSetRecoilState } from 'recoil';
|
||||
import { type APP_LOCALES, SOURCE_LOCALE } from 'twenty-shared/translations';
|
||||
import { SOURCE_LOCALE, type APP_LOCALES } from 'twenty-shared/translations';
|
||||
import { type ObjectPermissions } from 'twenty-shared/types';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
import { type ColorScheme } from 'twenty-ui/input';
|
||||
@@ -136,6 +136,7 @@ export const useLoadCurrentUser = () => {
|
||||
}, [
|
||||
getCurrentUser,
|
||||
isOnAWorkspace,
|
||||
setAvailableWorkspaces,
|
||||
setCurrentUser,
|
||||
setCurrentUserWorkspace,
|
||||
setCurrentWorkspace,
|
||||
@@ -143,7 +144,6 @@ export const useLoadCurrentUser = () => {
|
||||
setCurrentWorkspaceMembers,
|
||||
setDateTimeFormat,
|
||||
setLastAuthenticateWorkspaceDomain,
|
||||
setAvailableWorkspaces,
|
||||
]);
|
||||
|
||||
return {
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
import { gql } from '@apollo/client';
|
||||
|
||||
export const VIEW_FIELD_FRAGMENT = gql`
|
||||
fragment ViewFieldFragment on CoreViewField {
|
||||
id
|
||||
fieldMetadataId
|
||||
viewId
|
||||
isVisible
|
||||
position
|
||||
size
|
||||
aggregateOperation
|
||||
createdAt
|
||||
updatedAt
|
||||
}
|
||||
`;
|
||||
@@ -0,0 +1,16 @@
|
||||
import { gql } from '@apollo/client';
|
||||
|
||||
export const VIEW_FILTER_FRAGMENT = gql`
|
||||
fragment ViewFilterFragment on CoreViewFilter {
|
||||
id
|
||||
fieldMetadataId
|
||||
operand
|
||||
value
|
||||
viewFilterGroupId
|
||||
positionInViewFilterGroup
|
||||
subFieldName
|
||||
viewId
|
||||
createdAt
|
||||
updatedAt
|
||||
}
|
||||
`;
|
||||
@@ -0,0 +1,13 @@
|
||||
import { gql } from '@apollo/client';
|
||||
|
||||
export const VIEW_FILTER_GROUP_FRAGMENT = gql`
|
||||
fragment ViewFilterGroupFragment on CoreViewFilterGroup {
|
||||
id
|
||||
parentViewFilterGroupId
|
||||
logicalOperator
|
||||
positionInViewFilterGroup
|
||||
viewId
|
||||
createdAt
|
||||
updatedAt
|
||||
}
|
||||
`;
|
||||
@@ -0,0 +1,20 @@
|
||||
import { gql } from '@apollo/client';
|
||||
|
||||
export const VIEW_FRAGMENT = gql`
|
||||
fragment ViewFragment on CoreView {
|
||||
id
|
||||
name
|
||||
objectMetadataId
|
||||
type
|
||||
key
|
||||
icon
|
||||
position
|
||||
isCompact
|
||||
openRecordIn
|
||||
kanbanAggregateOperation
|
||||
kanbanAggregateOperationFieldMetadataId
|
||||
createdAt
|
||||
updatedAt
|
||||
anyFieldFilterValue
|
||||
}
|
||||
`;
|
||||
@@ -0,0 +1,14 @@
|
||||
import { gql } from '@apollo/client';
|
||||
|
||||
export const VIEW_GROUP_FRAGMENT = gql`
|
||||
fragment ViewGroupFragment on CoreViewGroup {
|
||||
id
|
||||
fieldMetadataId
|
||||
isVisible
|
||||
fieldValue
|
||||
position
|
||||
viewId
|
||||
createdAt
|
||||
updatedAt
|
||||
}
|
||||
`;
|
||||
@@ -0,0 +1,12 @@
|
||||
import { gql } from '@apollo/client';
|
||||
|
||||
export const VIEW_SORT_FRAGMENT = gql`
|
||||
fragment ViewSortFragment on CoreViewSort {
|
||||
id
|
||||
fieldMetadataId
|
||||
direction
|
||||
viewId
|
||||
createdAt
|
||||
updatedAt
|
||||
}
|
||||
`;
|
||||
@@ -0,0 +1,11 @@
|
||||
import { VIEW_FRAGMENT } from '@/views/graphql/fragments/viewFragment';
|
||||
import { gql } from '@apollo/client';
|
||||
|
||||
export const CREATE_CORE_VIEW = gql`
|
||||
${VIEW_FRAGMENT}
|
||||
mutation CreateCoreView($input: CreateViewInput!) {
|
||||
createCoreView(input: $input) {
|
||||
...ViewFragment
|
||||
}
|
||||
}
|
||||
`;
|
||||
@@ -0,0 +1,11 @@
|
||||
import { VIEW_FIELD_FRAGMENT } from '@/views/graphql/fragments/viewFieldFragment';
|
||||
import { gql } from '@apollo/client';
|
||||
|
||||
export const CREATE_CORE_VIEW_FIELD = gql`
|
||||
${VIEW_FIELD_FRAGMENT}
|
||||
mutation CreateCoreViewField($input: CreateViewFieldInput!) {
|
||||
createCoreViewField(input: $input) {
|
||||
...ViewFieldFragment
|
||||
}
|
||||
}
|
||||
`;
|
||||
@@ -0,0 +1,11 @@
|
||||
import { VIEW_FILTER_FRAGMENT } from '@/views/graphql/fragments/viewFilterFragment';
|
||||
import { gql } from '@apollo/client';
|
||||
|
||||
export const CREATE_CORE_VIEW_FILTER = gql`
|
||||
${VIEW_FILTER_FRAGMENT}
|
||||
mutation CreateCoreViewFilter($input: CreateViewFilterInput!) {
|
||||
createCoreViewFilter(input: $input) {
|
||||
...ViewFilterFragment
|
||||
}
|
||||
}
|
||||
`;
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
import { VIEW_FILTER_GROUP_FRAGMENT } from '@/views/graphql/fragments/viewFilterGroupFragment';
|
||||
import { gql } from '@apollo/client';
|
||||
|
||||
export const CREATE_CORE_VIEW_FILTER_GROUP = gql`
|
||||
${VIEW_FILTER_GROUP_FRAGMENT}
|
||||
mutation CreateCoreViewFilterGroup($input: CreateViewFilterGroupInput!) {
|
||||
createCoreViewFilterGroup(input: $input) {
|
||||
...ViewFilterGroupFragment
|
||||
}
|
||||
}
|
||||
`;
|
||||
@@ -0,0 +1,11 @@
|
||||
import { VIEW_GROUP_FRAGMENT } from '@/views/graphql/fragments/viewGroupFragment';
|
||||
import { gql } from '@apollo/client';
|
||||
|
||||
export const CREATE_CORE_VIEW_GROUP = gql`
|
||||
${VIEW_GROUP_FRAGMENT}
|
||||
mutation CreateCoreViewGroup($input: CreateViewGroupInput!) {
|
||||
createCoreViewGroup(input: $input) {
|
||||
...ViewGroupFragment
|
||||
}
|
||||
}
|
||||
`;
|
||||
@@ -0,0 +1,11 @@
|
||||
import { VIEW_SORT_FRAGMENT } from '@/views/graphql/fragments/viewSortFragment';
|
||||
import { gql } from '@apollo/client';
|
||||
|
||||
export const CREATE_CORE_VIEW_SORT = gql`
|
||||
${VIEW_SORT_FRAGMENT}
|
||||
mutation CreateCoreViewSort($input: CreateViewSortInput!) {
|
||||
createCoreViewSort(input: $input) {
|
||||
...ViewSortFragment
|
||||
}
|
||||
}
|
||||
`;
|
||||
@@ -0,0 +1,7 @@
|
||||
import { gql } from '@apollo/client';
|
||||
|
||||
export const DELETE_CORE_VIEW = gql`
|
||||
mutation DeleteCoreView($id: String!) {
|
||||
deleteCoreView(id: $id)
|
||||
}
|
||||
`;
|
||||
@@ -0,0 +1,7 @@
|
||||
import { gql } from '@apollo/client';
|
||||
|
||||
export const DELETE_CORE_VIEW_FIELD = gql`
|
||||
mutation DeleteCoreViewField($id: String!) {
|
||||
deleteCoreViewField(id: $id)
|
||||
}
|
||||
`;
|
||||
@@ -0,0 +1,7 @@
|
||||
import { gql } from '@apollo/client';
|
||||
|
||||
export const DELETE_CORE_VIEW_FILTER = gql`
|
||||
mutation DeleteCoreViewFilter($id: String!) {
|
||||
deleteCoreViewFilter(id: $id)
|
||||
}
|
||||
`;
|
||||
@@ -0,0 +1,7 @@
|
||||
import { gql } from '@apollo/client';
|
||||
|
||||
export const DELETE_CORE_VIEW_FILTER_GROUP = gql`
|
||||
mutation DeleteCoreViewFilterGroup($id: String!) {
|
||||
deleteCoreViewFilterGroup(id: $id)
|
||||
}
|
||||
`;
|
||||
@@ -0,0 +1,7 @@
|
||||
import { gql } from '@apollo/client';
|
||||
|
||||
export const DELETE_CORE_VIEW_GROUP = gql`
|
||||
mutation DeleteCoreViewGroup($id: String!) {
|
||||
deleteCoreViewGroup(id: $id)
|
||||
}
|
||||
`;
|
||||
@@ -0,0 +1,7 @@
|
||||
import { gql } from '@apollo/client';
|
||||
|
||||
export const DELETE_CORE_VIEW_SORT = gql`
|
||||
mutation DeleteCoreViewSort($id: String!) {
|
||||
deleteCoreViewSort(id: $id)
|
||||
}
|
||||
`;
|
||||
@@ -0,0 +1,7 @@
|
||||
import { gql } from '@apollo/client';
|
||||
|
||||
export const DESTROY_CORE_VIEW = gql`
|
||||
mutation DestroyCoreView($id: String!) {
|
||||
destroyCoreView(id: $id)
|
||||
}
|
||||
`;
|
||||
@@ -0,0 +1,7 @@
|
||||
import { gql } from '@apollo/client';
|
||||
|
||||
export const DESTROY_CORE_VIEW_FIELD = gql`
|
||||
mutation DestroyCoreViewField($id: String!) {
|
||||
destroyCoreViewField(id: $id)
|
||||
}
|
||||
`;
|
||||
@@ -0,0 +1,7 @@
|
||||
import { gql } from '@apollo/client';
|
||||
|
||||
export const DESTROY_CORE_VIEW_FILTER = gql`
|
||||
mutation DestroyCoreViewFilter($id: String!) {
|
||||
destroyCoreViewFilter(id: $id)
|
||||
}
|
||||
`;
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
import { gql } from '@apollo/client';
|
||||
|
||||
export const DESTROY_CORE_VIEW_FILTER_GROUP = gql`
|
||||
mutation DestroyCoreViewFilterGroup($id: String!) {
|
||||
destroyCoreViewFilterGroup(id: $id)
|
||||
}
|
||||
`;
|
||||
@@ -0,0 +1,7 @@
|
||||
import { gql } from '@apollo/client';
|
||||
|
||||
export const DESTROY_CORE_VIEW_GROUP = gql`
|
||||
mutation DestroyCoreViewGroup($id: String!) {
|
||||
destroyCoreViewGroup(id: $id)
|
||||
}
|
||||
`;
|
||||
@@ -0,0 +1,7 @@
|
||||
import { gql } from '@apollo/client';
|
||||
|
||||
export const DESTROY_CORE_VIEW_SORT = gql`
|
||||
mutation DestroyCoreViewSort($id: String!) {
|
||||
destroyCoreViewSort(id: $id)
|
||||
}
|
||||
`;
|
||||
@@ -0,0 +1,11 @@
|
||||
import { VIEW_FRAGMENT } from '@/views/graphql/fragments/viewFragment';
|
||||
import { gql } from '@apollo/client';
|
||||
|
||||
export const UPDATE_CORE_VIEW = gql`
|
||||
${VIEW_FRAGMENT}
|
||||
mutation UpdateCoreView($id: String!, $input: UpdateViewInput!) {
|
||||
updateCoreView(id: $id, input: $input) {
|
||||
...ViewFragment
|
||||
}
|
||||
}
|
||||
`;
|
||||
@@ -0,0 +1,11 @@
|
||||
import { VIEW_FIELD_FRAGMENT } from '@/views/graphql/fragments/viewFieldFragment';
|
||||
import { gql } from '@apollo/client';
|
||||
|
||||
export const UPDATE_CORE_VIEW_FIELD = gql`
|
||||
${VIEW_FIELD_FRAGMENT}
|
||||
mutation UpdateCoreViewField($id: String!, $input: UpdateViewFieldInput!) {
|
||||
updateCoreViewField(id: $id, input: $input) {
|
||||
...ViewFieldFragment
|
||||
}
|
||||
}
|
||||
`;
|
||||
@@ -0,0 +1,11 @@
|
||||
import { VIEW_FILTER_FRAGMENT } from '@/views/graphql/fragments/viewFilterFragment';
|
||||
import { gql } from '@apollo/client';
|
||||
|
||||
export const UPDATE_CORE_VIEW_FILTER = gql`
|
||||
${VIEW_FILTER_FRAGMENT}
|
||||
mutation UpdateCoreViewFilter($id: String!, $input: UpdateViewFilterInput!) {
|
||||
updateCoreViewFilter(id: $id, input: $input) {
|
||||
...ViewFilterFragment
|
||||
}
|
||||
}
|
||||
`;
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
import { VIEW_FILTER_GROUP_FRAGMENT } from '@/views/graphql/fragments/viewFilterGroupFragment';
|
||||
import { gql } from '@apollo/client';
|
||||
|
||||
export const UPDATE_CORE_VIEW_FILTER_GROUP = gql`
|
||||
${VIEW_FILTER_GROUP_FRAGMENT}
|
||||
mutation UpdateCoreViewFilterGroup(
|
||||
$id: String!
|
||||
$input: UpdateViewFilterGroupInput!
|
||||
) {
|
||||
updateCoreViewFilterGroup(id: $id, input: $input) {
|
||||
...ViewFilterGroupFragment
|
||||
}
|
||||
}
|
||||
`;
|
||||
@@ -0,0 +1,11 @@
|
||||
import { VIEW_GROUP_FRAGMENT } from '@/views/graphql/fragments/viewGroupFragment';
|
||||
import { gql } from '@apollo/client';
|
||||
|
||||
export const UPDATE_CORE_VIEW_GROUP = gql`
|
||||
${VIEW_GROUP_FRAGMENT}
|
||||
mutation UpdateCoreViewGroup($id: String!, $input: UpdateViewGroupInput!) {
|
||||
updateCoreViewGroup(id: $id, input: $input) {
|
||||
...ViewGroupFragment
|
||||
}
|
||||
}
|
||||
`;
|
||||
@@ -0,0 +1,11 @@
|
||||
import { VIEW_SORT_FRAGMENT } from '@/views/graphql/fragments/viewSortFragment';
|
||||
import { gql } from '@apollo/client';
|
||||
|
||||
export const UPDATE_CORE_VIEW_SORT = gql`
|
||||
${VIEW_SORT_FRAGMENT}
|
||||
mutation UpdateCoreViewSort($id: String!, $input: UpdateViewSortInput!) {
|
||||
updateCoreViewSort(id: $id, input: $input) {
|
||||
...ViewSortFragment
|
||||
}
|
||||
}
|
||||
`;
|
||||
@@ -0,0 +1,11 @@
|
||||
import { VIEW_FIELD_FRAGMENT } from '@/views/graphql/fragments/viewFieldFragment';
|
||||
import { gql } from '@apollo/client';
|
||||
|
||||
export const FIND_MANY_CORE_VIEW_FIELDS = gql`
|
||||
${VIEW_FIELD_FRAGMENT}
|
||||
query FindManyCoreViewFields($viewId: String!) {
|
||||
getCoreViewFields(viewId: $viewId) {
|
||||
...ViewFieldFragment
|
||||
}
|
||||
}
|
||||
`;
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
import { VIEW_FILTER_GROUP_FRAGMENT } from '@/views/graphql/fragments/viewFilterGroupFragment';
|
||||
import { gql } from '@apollo/client';
|
||||
|
||||
export const FIND_MANY_CORE_VIEW_FILTER_GROUPS = gql`
|
||||
${VIEW_FILTER_GROUP_FRAGMENT}
|
||||
query FindManyCoreViewFilterGroups($viewId: String) {
|
||||
getCoreViewFilterGroups(viewId: $viewId) {
|
||||
...ViewFilterGroupFragment
|
||||
}
|
||||
}
|
||||
`;
|
||||
@@ -0,0 +1,11 @@
|
||||
import { VIEW_FILTER_FRAGMENT } from '@/views/graphql/fragments/viewFilterFragment';
|
||||
import { gql } from '@apollo/client';
|
||||
|
||||
export const FIND_MANY_CORE_VIEW_FILTERS = gql`
|
||||
${VIEW_FILTER_FRAGMENT}
|
||||
query FindManyCoreViewFilters($viewId: String) {
|
||||
getCoreViewFilters(viewId: $viewId) {
|
||||
...ViewFilterFragment
|
||||
}
|
||||
}
|
||||
`;
|
||||
@@ -0,0 +1,11 @@
|
||||
import { VIEW_GROUP_FRAGMENT } from '@/views/graphql/fragments/viewGroupFragment';
|
||||
import { gql } from '@apollo/client';
|
||||
|
||||
export const FIND_MANY_CORE_VIEW_GROUPS = gql`
|
||||
${VIEW_GROUP_FRAGMENT}
|
||||
query FindManyCoreViewGroups($viewId: String) {
|
||||
getCoreViewGroups(viewId: $viewId) {
|
||||
...ViewGroupFragment
|
||||
}
|
||||
}
|
||||
`;
|
||||
@@ -0,0 +1,11 @@
|
||||
import { VIEW_SORT_FRAGMENT } from '@/views/graphql/fragments/viewSortFragment';
|
||||
import { gql } from '@apollo/client';
|
||||
|
||||
export const FIND_MANY_CORE_VIEW_SORTS = gql`
|
||||
${VIEW_SORT_FRAGMENT}
|
||||
query FindManyCoreViewSorts($viewId: String) {
|
||||
getCoreViewSorts(viewId: $viewId) {
|
||||
...ViewSortFragment
|
||||
}
|
||||
}
|
||||
`;
|
||||
@@ -0,0 +1,11 @@
|
||||
import { VIEW_FRAGMENT } from '@/views/graphql/fragments/viewFragment';
|
||||
import { gql } from '@apollo/client';
|
||||
|
||||
export const FIND_MANY_CORE_VIEWS = gql`
|
||||
${VIEW_FRAGMENT}
|
||||
query FindManyCoreViews($objectMetadataId: String) {
|
||||
getCoreViews(objectMetadataId: $objectMetadataId) {
|
||||
...ViewFragment
|
||||
}
|
||||
}
|
||||
`;
|
||||
@@ -0,0 +1,11 @@
|
||||
import { VIEW_FRAGMENT } from '@/views/graphql/fragments/viewFragment';
|
||||
import { gql } from '@apollo/client';
|
||||
|
||||
export const FIND_ONE_CORE_VIEW = gql`
|
||||
${VIEW_FRAGMENT}
|
||||
query FindOneCoreView($id: String!) {
|
||||
getCoreView(id: $id) {
|
||||
...ViewFragment
|
||||
}
|
||||
}
|
||||
`;
|
||||
@@ -0,0 +1,11 @@
|
||||
import { VIEW_FIELD_FRAGMENT } from '@/views/graphql/fragments/viewFieldFragment';
|
||||
import { gql } from '@apollo/client';
|
||||
|
||||
export const FIND_ONE_CORE_VIEW_FIELD = gql`
|
||||
${VIEW_FIELD_FRAGMENT}
|
||||
query FindOneCoreViewField($id: String!) {
|
||||
getCoreViewField(id: $id) {
|
||||
...ViewFieldFragment
|
||||
}
|
||||
}
|
||||
`;
|
||||
@@ -0,0 +1,11 @@
|
||||
import { VIEW_FILTER_FRAGMENT } from '@/views/graphql/fragments/viewFilterFragment';
|
||||
import { gql } from '@apollo/client';
|
||||
|
||||
export const FIND_ONE_CORE_VIEW_FILTER = gql`
|
||||
${VIEW_FILTER_FRAGMENT}
|
||||
query FindOneCoreViewFilter($id: String!) {
|
||||
getCoreViewFilter(id: $id) {
|
||||
...ViewFilterFragment
|
||||
}
|
||||
}
|
||||
`;
|
||||
@@ -0,0 +1,11 @@
|
||||
import { VIEW_FILTER_GROUP_FRAGMENT } from '@/views/graphql/fragments/viewFilterGroupFragment';
|
||||
import { gql } from '@apollo/client';
|
||||
|
||||
export const FIND_ONE_CORE_VIEW_FILTER_GROUP = gql`
|
||||
${VIEW_FILTER_GROUP_FRAGMENT}
|
||||
query FindOneCoreViewFilterGroup($id: String!) {
|
||||
getCoreViewFilterGroup(id: $id) {
|
||||
...ViewFilterGroupFragment
|
||||
}
|
||||
}
|
||||
`;
|
||||
@@ -0,0 +1,11 @@
|
||||
import { VIEW_GROUP_FRAGMENT } from '@/views/graphql/fragments/viewGroupFragment';
|
||||
import { gql } from '@apollo/client';
|
||||
|
||||
export const FIND_ONE_CORE_VIEW_GROUP = gql`
|
||||
${VIEW_GROUP_FRAGMENT}
|
||||
query FindOneCoreViewGroup($id: String!) {
|
||||
getCoreViewGroup(id: $id) {
|
||||
...ViewGroupFragment
|
||||
}
|
||||
}
|
||||
`;
|
||||
@@ -0,0 +1,11 @@
|
||||
import { VIEW_SORT_FRAGMENT } from '@/views/graphql/fragments/viewSortFragment';
|
||||
import { gql } from '@apollo/client';
|
||||
|
||||
export const FIND_ONE_CORE_VIEW_SORT = gql`
|
||||
${VIEW_SORT_FRAGMENT}
|
||||
query FindOneCoreViewSort($id: String!) {
|
||||
getCoreViewSort(id: $id) {
|
||||
...ViewSortFragment
|
||||
}
|
||||
}
|
||||
`;
|
||||
+101
-3
@@ -11,12 +11,20 @@ import { useGetRecordFromCache } from '@/object-record/cache/hooks/useGetRecordF
|
||||
import { useCreateOneRecordMutation } from '@/object-record/hooks/useCreateOneRecordMutation';
|
||||
import { useObjectPermissions } from '@/object-record/hooks/useObjectPermissions';
|
||||
import { useUpdateOneRecordMutation } from '@/object-record/hooks/useUpdateOneRecordMutation';
|
||||
import { CREATE_CORE_VIEW_FIELD } from '@/views/graphql/mutations/createCoreViewField';
|
||||
import { UPDATE_CORE_VIEW_FIELD } from '@/views/graphql/mutations/updateCoreViewField';
|
||||
import { type GraphQLView } from '@/views/types/GraphQLView';
|
||||
import { type ViewField } from '@/views/types/ViewField';
|
||||
import { useFeatureFlagsMap } from '@/workspace/hooks/useFeatureFlagsMap';
|
||||
import { useApolloClient } from '@apollo/client';
|
||||
import { isNull } from '@sniptt/guards';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
import { FeatureFlagKey, type CoreViewField } from '~/generated/graphql';
|
||||
|
||||
export const usePersistViewFieldRecords = () => {
|
||||
const featureFlags = useFeatureFlagsMap();
|
||||
const isCoreViewEnabled = featureFlags[FeatureFlagKey.IS_CORE_VIEW_ENABLED];
|
||||
|
||||
const { objectMetadataItem } = useObjectMetadataItem({
|
||||
objectNameSingular: CoreObjectNameSingular.ViewField,
|
||||
});
|
||||
@@ -36,11 +44,12 @@ export const usePersistViewFieldRecords = () => {
|
||||
const { objectMetadataItems } = useObjectMetadataItems();
|
||||
const { objectPermissionsByObjectMetadataId } = useObjectPermissions();
|
||||
const apolloCoreClient = useApolloCoreClient();
|
||||
const apolloClient = useApolloClient();
|
||||
|
||||
const createViewFieldRecords = useCallback(
|
||||
(
|
||||
viewFieldsToCreate: Omit<ViewField, 'definition'>[],
|
||||
view: GraphQLView,
|
||||
view: Pick<GraphQLView, 'id'>,
|
||||
) => {
|
||||
if (!viewFieldsToCreate.length) return;
|
||||
return Promise.all(
|
||||
@@ -130,8 +139,97 @@ export const usePersistViewFieldRecords = () => {
|
||||
],
|
||||
);
|
||||
|
||||
const createCoreViewFieldRecords = useCallback(
|
||||
(
|
||||
viewFieldsToCreate: Omit<ViewField, 'definition'>[],
|
||||
view: Pick<GraphQLView, 'id'>,
|
||||
) => {
|
||||
if (!viewFieldsToCreate.length) return;
|
||||
return Promise.all(
|
||||
viewFieldsToCreate.map((viewField) =>
|
||||
apolloClient.mutate({
|
||||
mutation: CREATE_CORE_VIEW_FIELD,
|
||||
variables: {
|
||||
input: {
|
||||
fieldMetadataId: viewField.fieldMetadataId,
|
||||
viewId: view.id,
|
||||
isVisible: viewField.isVisible,
|
||||
position: viewField.position,
|
||||
size: viewField.size,
|
||||
} satisfies Partial<CoreViewField>,
|
||||
},
|
||||
update: (cache, { data }) => {
|
||||
const record = data?.['createCoreViewField'];
|
||||
if (!record) return;
|
||||
|
||||
triggerCreateRecordsOptimisticEffect({
|
||||
cache,
|
||||
objectMetadataItem,
|
||||
recordsToCreate: [record],
|
||||
objectMetadataItems,
|
||||
objectPermissionsByObjectMetadataId,
|
||||
});
|
||||
},
|
||||
}),
|
||||
),
|
||||
);
|
||||
},
|
||||
[
|
||||
apolloClient,
|
||||
objectMetadataItem,
|
||||
objectMetadataItems,
|
||||
objectPermissionsByObjectMetadataId,
|
||||
],
|
||||
);
|
||||
|
||||
const updateCoreViewFieldRecords = useCallback(
|
||||
(viewFieldsToUpdate: Omit<ViewField, 'definition'>[]) => {
|
||||
if (!viewFieldsToUpdate.length) return;
|
||||
|
||||
return Promise.all(
|
||||
viewFieldsToUpdate.map((viewField) =>
|
||||
apolloClient.mutate({
|
||||
mutation: UPDATE_CORE_VIEW_FIELD,
|
||||
variables: {
|
||||
idToUpdate: viewField.id,
|
||||
input: {
|
||||
isVisible: viewField.isVisible,
|
||||
position: viewField.position,
|
||||
size: viewField.size,
|
||||
aggregateOperation: viewField.aggregateOperation,
|
||||
} satisfies Partial<CoreViewField>,
|
||||
},
|
||||
update: (cache, { data }) => {
|
||||
const record = data?.['updateCoreViewField'];
|
||||
if (!isDefined(record)) return;
|
||||
|
||||
const cachedRecord = getRecordFromCache<ViewField>(
|
||||
record.id,
|
||||
cache,
|
||||
);
|
||||
if (isNull(cachedRecord)) return;
|
||||
|
||||
triggerUpdateRecordOptimisticEffect({
|
||||
cache,
|
||||
objectMetadataItem,
|
||||
currentRecord: cachedRecord,
|
||||
updatedRecord: record,
|
||||
objectMetadataItems,
|
||||
});
|
||||
},
|
||||
}),
|
||||
),
|
||||
);
|
||||
},
|
||||
[apolloClient, getRecordFromCache, objectMetadataItem, objectMetadataItems],
|
||||
);
|
||||
|
||||
return {
|
||||
createViewFieldRecords,
|
||||
updateViewFieldRecords,
|
||||
createViewFieldRecords: isCoreViewEnabled
|
||||
? createCoreViewFieldRecords
|
||||
: createViewFieldRecords,
|
||||
updateViewFieldRecords: isCoreViewEnabled
|
||||
? updateCoreViewFieldRecords
|
||||
: updateViewFieldRecords,
|
||||
};
|
||||
};
|
||||
|
||||
+182
-5
@@ -12,11 +12,21 @@ import { useCreateOneRecordMutation } from '@/object-record/hooks/useCreateOneRe
|
||||
import { useDestroyOneRecordMutation } from '@/object-record/hooks/useDestroyOneRecordMutation';
|
||||
import { useObjectPermissions } from '@/object-record/hooks/useObjectPermissions';
|
||||
import { useUpdateOneRecordMutation } from '@/object-record/hooks/useUpdateOneRecordMutation';
|
||||
import { CREATE_CORE_VIEW_FILTER_GROUP } from '@/views/graphql/mutations/createCoreViewFilterGroup';
|
||||
import { DESTROY_CORE_VIEW_FILTER_GROUP } from '@/views/graphql/mutations/destroyCoreViewFilterGroup';
|
||||
import { UPDATE_CORE_VIEW_FILTER_GROUP } from '@/views/graphql/mutations/updateCoreViewFilterGroup';
|
||||
import { type GraphQLView } from '@/views/types/GraphQLView';
|
||||
import { type ViewFilterGroup } from '@/views/types/ViewFilterGroup';
|
||||
import { useFeatureFlagsMap } from '@/workspace/hooks/useFeatureFlagsMap';
|
||||
import { useApolloClient } from '@apollo/client';
|
||||
import { isNull } from '@sniptt/guards';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
import { type CoreViewFilterGroup, FeatureFlagKey } from '~/generated/graphql';
|
||||
|
||||
export const usePersistViewFilterGroupRecords = () => {
|
||||
const featureFlags = useFeatureFlagsMap();
|
||||
const isCoreViewEnabled = featureFlags[FeatureFlagKey.IS_CORE_VIEW_ENABLED];
|
||||
|
||||
const { objectMetadataItem } = useObjectMetadataItem({
|
||||
objectNameSingular: CoreObjectNameSingular.ViewFilterGroup,
|
||||
});
|
||||
@@ -40,9 +50,10 @@ export const usePersistViewFilterGroupRecords = () => {
|
||||
const { objectMetadataItems } = useObjectMetadataItems();
|
||||
const { objectPermissionsByObjectMetadataId } = useObjectPermissions();
|
||||
const apolloCoreClient = useApolloCoreClient();
|
||||
const apolloClient = useApolloClient();
|
||||
|
||||
const createViewFilterGroupRecord = useCallback(
|
||||
async (viewFilterGroup: ViewFilterGroup, view: GraphQLView) => {
|
||||
async (viewFilterGroup: ViewFilterGroup, view: Pick<GraphQLView, 'id'>) => {
|
||||
const result = await apolloCoreClient.mutate<{
|
||||
createViewFilterGroup: ViewFilterGroup;
|
||||
}>({
|
||||
@@ -87,7 +98,10 @@ export const usePersistViewFilterGroupRecords = () => {
|
||||
);
|
||||
|
||||
const createViewFilterGroupRecords = useCallback(
|
||||
async (viewFilterGroupsToCreate: ViewFilterGroup[], view: GraphQLView) => {
|
||||
async (
|
||||
viewFilterGroupsToCreate: ViewFilterGroup[],
|
||||
view: Pick<GraphQLView, 'id'>,
|
||||
) => {
|
||||
if (!viewFilterGroupsToCreate.length) return [];
|
||||
|
||||
const oldToNewId = new Map<string, string>();
|
||||
@@ -209,9 +223,172 @@ export const usePersistViewFilterGroupRecords = () => {
|
||||
],
|
||||
);
|
||||
|
||||
const createCoreViewFilterGroupRecord = useCallback(
|
||||
async (viewFilterGroup: ViewFilterGroup, view: Pick<GraphQLView, 'id'>) => {
|
||||
const result = await apolloClient.mutate<{
|
||||
createCoreViewFilterGroup: ViewFilterGroup;
|
||||
}>({
|
||||
mutation: CREATE_CORE_VIEW_FILTER_GROUP,
|
||||
variables: {
|
||||
input: {
|
||||
viewId: view.id,
|
||||
parentViewFilterGroupId: viewFilterGroup.parentViewFilterGroupId,
|
||||
logicalOperator: viewFilterGroup.logicalOperator,
|
||||
positionInViewFilterGroup:
|
||||
viewFilterGroup.positionInViewFilterGroup,
|
||||
} satisfies Partial<CoreViewFilterGroup>,
|
||||
},
|
||||
update: (cache, { data }) => {
|
||||
const record = data?.createCoreViewFilterGroup;
|
||||
if (!isDefined(record)) return;
|
||||
|
||||
triggerCreateRecordsOptimisticEffect({
|
||||
cache,
|
||||
objectMetadataItem,
|
||||
recordsToCreate: [record],
|
||||
objectMetadataItems,
|
||||
objectPermissionsByObjectMetadataId,
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
if (!result.data) {
|
||||
throw new Error('Failed to create core view filter group');
|
||||
}
|
||||
|
||||
return { newRecordId: result.data.createCoreViewFilterGroup.id };
|
||||
},
|
||||
[
|
||||
apolloClient,
|
||||
objectMetadataItem,
|
||||
objectMetadataItems,
|
||||
objectPermissionsByObjectMetadataId,
|
||||
],
|
||||
);
|
||||
|
||||
const createCoreViewFilterGroupRecords = useCallback(
|
||||
async (
|
||||
viewFilterGroupsToCreate: ViewFilterGroup[],
|
||||
view: Pick<GraphQLView, 'id'>,
|
||||
) => {
|
||||
if (!viewFilterGroupsToCreate.length) return [];
|
||||
|
||||
const oldToNewId = new Map<string, string>();
|
||||
|
||||
for (const viewFilterGroupToCreate of viewFilterGroupsToCreate) {
|
||||
const newParentViewFilterGroupId = isDefined(
|
||||
viewFilterGroupToCreate.parentViewFilterGroupId,
|
||||
)
|
||||
? (oldToNewId.get(viewFilterGroupToCreate.parentViewFilterGroupId) ??
|
||||
viewFilterGroupToCreate.parentViewFilterGroupId)
|
||||
: undefined;
|
||||
|
||||
const { newRecordId } = await createCoreViewFilterGroupRecord(
|
||||
{
|
||||
...viewFilterGroupToCreate,
|
||||
parentViewFilterGroupId: newParentViewFilterGroupId,
|
||||
},
|
||||
view,
|
||||
);
|
||||
|
||||
oldToNewId.set(viewFilterGroupToCreate.id, newRecordId);
|
||||
}
|
||||
|
||||
const newRecordIds = viewFilterGroupsToCreate.map((viewFilterGroup) => {
|
||||
const newId = oldToNewId.get(viewFilterGroup.id);
|
||||
if (!newId) {
|
||||
throw new Error('Failed to create core view filter group');
|
||||
}
|
||||
return newId;
|
||||
});
|
||||
|
||||
return newRecordIds;
|
||||
},
|
||||
[createCoreViewFilterGroupRecord],
|
||||
);
|
||||
|
||||
const updateCoreViewFilterGroupRecords = useCallback(
|
||||
(viewFilterGroupsToUpdate: ViewFilterGroup[]) => {
|
||||
if (!viewFilterGroupsToUpdate.length) return;
|
||||
return Promise.all(
|
||||
viewFilterGroupsToUpdate.map((viewFilterGroup) =>
|
||||
apolloClient.mutate<{ updateCoreViewFilterGroup: ViewFilterGroup }>({
|
||||
mutation: UPDATE_CORE_VIEW_FILTER_GROUP,
|
||||
variables: {
|
||||
idToUpdate: viewFilterGroup.id,
|
||||
input: {
|
||||
parentViewFilterGroupId:
|
||||
viewFilterGroup.parentViewFilterGroupId,
|
||||
logicalOperator: viewFilterGroup.logicalOperator,
|
||||
positionInViewFilterGroup:
|
||||
viewFilterGroup.positionInViewFilterGroup,
|
||||
} satisfies Partial<CoreViewFilterGroup>,
|
||||
},
|
||||
update: (cache, { data }) => {
|
||||
const record = data?.updateCoreViewFilterGroup;
|
||||
if (!isDefined(record)) return;
|
||||
|
||||
const cachedRecord = getRecordFromCache<ViewFilterGroup>(
|
||||
record.id,
|
||||
cache,
|
||||
);
|
||||
if (isNull(cachedRecord)) return;
|
||||
|
||||
triggerUpdateRecordOptimisticEffect({
|
||||
cache,
|
||||
objectMetadataItem,
|
||||
currentRecord: cachedRecord,
|
||||
updatedRecord: record,
|
||||
objectMetadataItems,
|
||||
});
|
||||
},
|
||||
}),
|
||||
),
|
||||
);
|
||||
},
|
||||
[apolloClient, getRecordFromCache, objectMetadataItem, objectMetadataItems],
|
||||
);
|
||||
|
||||
const deleteCoreViewFilterGroupRecords = useCallback(
|
||||
(viewFilterGroupIdsToDelete: string[]) => {
|
||||
if (!viewFilterGroupIdsToDelete.length) return;
|
||||
return Promise.all(
|
||||
viewFilterGroupIdsToDelete.map((viewFilterGroupId) =>
|
||||
apolloClient.mutate<{ destroyCoreViewFilterGroup: ViewFilterGroup }>({
|
||||
mutation: DESTROY_CORE_VIEW_FILTER_GROUP,
|
||||
variables: {
|
||||
idToDestroy: viewFilterGroupId,
|
||||
},
|
||||
update: (cache, { data }) => {
|
||||
const record = data?.destroyCoreViewFilterGroup;
|
||||
if (!isDefined(record)) return;
|
||||
|
||||
const cachedRecord = getRecordFromCache(record.id, cache);
|
||||
if (isNull(cachedRecord)) return;
|
||||
|
||||
triggerDestroyRecordsOptimisticEffect({
|
||||
cache,
|
||||
objectMetadataItem,
|
||||
recordsToDestroy: [cachedRecord],
|
||||
objectMetadataItems,
|
||||
});
|
||||
},
|
||||
}),
|
||||
),
|
||||
);
|
||||
},
|
||||
[apolloClient, getRecordFromCache, objectMetadataItem, objectMetadataItems],
|
||||
);
|
||||
|
||||
return {
|
||||
createViewFilterGroupRecords,
|
||||
updateViewFilterGroupRecords,
|
||||
deleteViewFilterGroupRecords,
|
||||
createViewFilterGroupRecords: isCoreViewEnabled
|
||||
? createCoreViewFilterGroupRecords
|
||||
: createViewFilterGroupRecords,
|
||||
updateViewFilterGroupRecords: isCoreViewEnabled
|
||||
? updateCoreViewFilterGroupRecords
|
||||
: updateViewFilterGroupRecords,
|
||||
deleteViewFilterGroupRecords: isCoreViewEnabled
|
||||
? deleteCoreViewFilterGroupRecords
|
||||
: deleteViewFilterGroupRecords,
|
||||
};
|
||||
};
|
||||
|
||||
+141
-5
@@ -12,11 +12,22 @@ import { useCreateOneRecordMutation } from '@/object-record/hooks/useCreateOneRe
|
||||
import { useDestroyOneRecordMutation } from '@/object-record/hooks/useDestroyOneRecordMutation';
|
||||
import { useObjectPermissions } from '@/object-record/hooks/useObjectPermissions';
|
||||
import { useUpdateOneRecordMutation } from '@/object-record/hooks/useUpdateOneRecordMutation';
|
||||
import { CREATE_CORE_VIEW_FILTER } from '@/views/graphql/mutations/createCoreViewFilter';
|
||||
import { DESTROY_CORE_VIEW_FILTER } from '@/views/graphql/mutations/destroyCoreViewFilter';
|
||||
import { UPDATE_CORE_VIEW_FILTER } from '@/views/graphql/mutations/updateCoreViewFilter';
|
||||
import { type GraphQLView } from '@/views/types/GraphQLView';
|
||||
import { type ViewFilter } from '@/views/types/ViewFilter';
|
||||
import { convertViewFilterOperandToCore } from '@/views/utils/convertViewFilterOperandToCore';
|
||||
import { useFeatureFlagsMap } from '@/workspace/hooks/useFeatureFlagsMap';
|
||||
import { useApolloClient } from '@apollo/client';
|
||||
import { isNull } from '@sniptt/guards';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
import { type CoreViewFilter, FeatureFlagKey } from '~/generated/graphql';
|
||||
|
||||
export const usePersistViewFilterRecords = () => {
|
||||
const featureFlags = useFeatureFlagsMap();
|
||||
const isCoreViewEnabled = featureFlags[FeatureFlagKey.IS_CORE_VIEW_ENABLED];
|
||||
|
||||
const { objectMetadataItem } = useObjectMetadataItem({
|
||||
objectNameSingular: CoreObjectNameSingular.ViewFilter,
|
||||
});
|
||||
@@ -40,9 +51,9 @@ export const usePersistViewFilterRecords = () => {
|
||||
const { objectMetadataItems } = useObjectMetadataItems();
|
||||
const { objectPermissionsByObjectMetadataId } = useObjectPermissions();
|
||||
const apolloCoreClient = useApolloCoreClient();
|
||||
|
||||
const apolloClient = useApolloClient();
|
||||
const createViewFilterRecords = useCallback(
|
||||
(viewFiltersToCreate: ViewFilter[], view: GraphQLView) => {
|
||||
(viewFiltersToCreate: ViewFilter[], view: Pick<GraphQLView, 'id'>) => {
|
||||
if (viewFiltersToCreate.length === 0) return;
|
||||
|
||||
return Promise.all(
|
||||
@@ -176,9 +187,134 @@ export const usePersistViewFilterRecords = () => {
|
||||
],
|
||||
);
|
||||
|
||||
const createCoreViewFilterRecords = useCallback(
|
||||
(viewFiltersToCreate: ViewFilter[], view: Pick<GraphQLView, 'id'>) => {
|
||||
if (viewFiltersToCreate.length === 0) return;
|
||||
|
||||
return Promise.all(
|
||||
viewFiltersToCreate.map((viewFilter) =>
|
||||
apolloClient.mutate({
|
||||
mutation: CREATE_CORE_VIEW_FILTER,
|
||||
variables: {
|
||||
input: {
|
||||
fieldMetadataId: viewFilter.fieldMetadataId,
|
||||
viewId: view.id,
|
||||
value: viewFilter.value,
|
||||
operand: convertViewFilterOperandToCore(viewFilter.operand),
|
||||
viewFilterGroupId: viewFilter.viewFilterGroupId,
|
||||
positionInViewFilterGroup: viewFilter.positionInViewFilterGroup,
|
||||
subFieldName: viewFilter.subFieldName ?? null,
|
||||
} satisfies Partial<CoreViewFilter>,
|
||||
},
|
||||
update: (cache, { data }) => {
|
||||
const record = data?.['createCoreViewFilter'];
|
||||
if (!isDefined(record)) return;
|
||||
|
||||
triggerCreateRecordsOptimisticEffect({
|
||||
cache,
|
||||
objectMetadataItem,
|
||||
recordsToCreate: [record],
|
||||
objectMetadataItems,
|
||||
objectPermissionsByObjectMetadataId,
|
||||
});
|
||||
},
|
||||
}),
|
||||
),
|
||||
);
|
||||
},
|
||||
[
|
||||
apolloClient,
|
||||
objectMetadataItem,
|
||||
objectMetadataItems,
|
||||
objectPermissionsByObjectMetadataId,
|
||||
],
|
||||
);
|
||||
|
||||
const updateCoreViewFilterRecords = useCallback(
|
||||
(viewFiltersToUpdate: ViewFilter[]) => {
|
||||
if (!viewFiltersToUpdate.length) return;
|
||||
return Promise.all(
|
||||
viewFiltersToUpdate.map((viewFilter) =>
|
||||
apolloClient.mutate({
|
||||
mutation: UPDATE_CORE_VIEW_FILTER,
|
||||
variables: {
|
||||
idToUpdate: viewFilter.id,
|
||||
input: {
|
||||
value: viewFilter.value,
|
||||
operand: convertViewFilterOperandToCore(viewFilter.operand),
|
||||
positionInViewFilterGroup: viewFilter.positionInViewFilterGroup,
|
||||
viewFilterGroupId: viewFilter.viewFilterGroupId,
|
||||
subFieldName: viewFilter.subFieldName ?? null,
|
||||
} satisfies Partial<CoreViewFilter>,
|
||||
},
|
||||
update: (cache, { data }) => {
|
||||
const record = data?.['updateCoreViewFilter'];
|
||||
if (!isDefined(record)) return;
|
||||
|
||||
const cachedRecord = getRecordFromCache<ViewFilter>(
|
||||
record.id,
|
||||
cache,
|
||||
);
|
||||
if (isNull(cachedRecord)) return;
|
||||
|
||||
triggerUpdateRecordOptimisticEffect({
|
||||
cache,
|
||||
objectMetadataItem,
|
||||
currentRecord: cachedRecord,
|
||||
updatedRecord: record,
|
||||
objectMetadataItems,
|
||||
});
|
||||
},
|
||||
}),
|
||||
),
|
||||
);
|
||||
},
|
||||
[apolloClient, getRecordFromCache, objectMetadataItem, objectMetadataItems],
|
||||
);
|
||||
|
||||
const deleteCoreViewFilterRecords = useCallback(
|
||||
(viewFilterIdsToDelete: string[]) => {
|
||||
if (!viewFilterIdsToDelete.length) return;
|
||||
return Promise.all(
|
||||
viewFilterIdsToDelete.map((viewFilterId) =>
|
||||
apolloClient.mutate({
|
||||
mutation: DESTROY_CORE_VIEW_FILTER,
|
||||
variables: {
|
||||
idToDestroy: viewFilterId,
|
||||
},
|
||||
update: (cache, { data }) => {
|
||||
const record = data?.['destroyCoreViewFilter'];
|
||||
if (!isDefined(record)) return;
|
||||
|
||||
const cachedRecord = getRecordFromCache<ViewFilter>(
|
||||
record.id,
|
||||
cache,
|
||||
);
|
||||
if (isNull(cachedRecord)) return;
|
||||
|
||||
triggerDestroyRecordsOptimisticEffect({
|
||||
cache,
|
||||
objectMetadataItem,
|
||||
recordsToDestroy: [cachedRecord],
|
||||
objectMetadataItems,
|
||||
});
|
||||
},
|
||||
}),
|
||||
),
|
||||
);
|
||||
},
|
||||
[apolloClient, getRecordFromCache, objectMetadataItem, objectMetadataItems],
|
||||
);
|
||||
|
||||
return {
|
||||
createViewFilterRecords,
|
||||
updateViewFilterRecords,
|
||||
deleteViewFilterRecords,
|
||||
createViewFilterRecords: isCoreViewEnabled
|
||||
? createCoreViewFilterRecords
|
||||
: createViewFilterRecords,
|
||||
updateViewFilterRecords: isCoreViewEnabled
|
||||
? updateCoreViewFilterRecords
|
||||
: updateViewFilterRecords,
|
||||
deleteViewFilterRecords: isCoreViewEnabled
|
||||
? deleteCoreViewFilterRecords
|
||||
: deleteViewFilterRecords,
|
||||
};
|
||||
};
|
||||
|
||||
+103
-3
@@ -5,14 +5,25 @@ import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSi
|
||||
import { useCreateManyRecords } from '@/object-record/hooks/useCreateManyRecords';
|
||||
import { useDestroyManyRecords } from '@/object-record/hooks/useDestroyManyRecords';
|
||||
import { useUpdateOneRecordMutation } from '@/object-record/hooks/useUpdateOneRecordMutation';
|
||||
import { CREATE_CORE_VIEW_GROUP } from '@/views/graphql/mutations/createCoreViewGroup';
|
||||
import { DESTROY_CORE_VIEW_GROUP } from '@/views/graphql/mutations/destroyCoreViewGroup';
|
||||
import { UPDATE_CORE_VIEW_GROUP } from '@/views/graphql/mutations/updateCoreViewGroup';
|
||||
import { type ViewGroup } from '@/views/types/ViewGroup';
|
||||
import { useFeatureFlagsMap } from '@/workspace/hooks/useFeatureFlagsMap';
|
||||
import { useApolloClient } from '@apollo/client';
|
||||
import { FeatureFlagKey } from '~/generated/graphql';
|
||||
|
||||
type CreateViewGroupRecordsArgs = {
|
||||
viewGroupsToCreate: ViewGroup[];
|
||||
viewId: string;
|
||||
};
|
||||
|
||||
export const usePersistViewGroupRecords = () => {
|
||||
const featureFlags = useFeatureFlagsMap();
|
||||
const isCoreViewEnabled = featureFlags[FeatureFlagKey.IS_CORE_VIEW_ENABLED];
|
||||
|
||||
const apolloCoreClient = useApolloCoreClient();
|
||||
const apolloClient = useApolloClient();
|
||||
|
||||
const { createManyRecords } = useCreateManyRecords({
|
||||
objectNameSingular: CoreObjectNameSingular.ViewGroup,
|
||||
@@ -98,9 +109,98 @@ export const usePersistViewGroupRecords = () => {
|
||||
[destroyManyRecords],
|
||||
);
|
||||
|
||||
const createCoreViewGroupRecords = useCallback(
|
||||
({ viewGroupsToCreate, viewId }: CreateViewGroupRecordsArgs) => {
|
||||
if (viewGroupsToCreate.length === 0) return;
|
||||
|
||||
return Promise.all(
|
||||
viewGroupsToCreate.map((viewGroup) =>
|
||||
apolloClient.mutate({
|
||||
mutation: CREATE_CORE_VIEW_GROUP,
|
||||
variables: {
|
||||
input: {
|
||||
viewId,
|
||||
isVisible: viewGroup.isVisible,
|
||||
position: viewGroup.position,
|
||||
},
|
||||
},
|
||||
}),
|
||||
),
|
||||
);
|
||||
},
|
||||
[apolloClient],
|
||||
);
|
||||
|
||||
const updateCoreViewGroupRecords = useCallback(
|
||||
async (viewGroupsToUpdate: ViewGroup[]) => {
|
||||
if (!viewGroupsToUpdate.length) return;
|
||||
|
||||
const mutationPromises = viewGroupsToUpdate.map((viewGroup) =>
|
||||
apolloClient.mutate<{ updateCoreViewGroup: ViewGroup }>({
|
||||
mutation: UPDATE_CORE_VIEW_GROUP,
|
||||
variables: {
|
||||
idToUpdate: viewGroup.id,
|
||||
input: {
|
||||
isVisible: viewGroup.isVisible,
|
||||
position: viewGroup.position,
|
||||
},
|
||||
},
|
||||
// Avoid cache being updated with stale data
|
||||
fetchPolicy: 'no-cache',
|
||||
}),
|
||||
);
|
||||
|
||||
const mutationResults = await Promise.all(mutationPromises);
|
||||
|
||||
// FixMe: Using useUpdateOneRecord hook that call triggerUpdateRecordsOptimisticEffect is actaully causing multiple records to be created
|
||||
// This is a temporary fix
|
||||
mutationResults.forEach(({ data }) => {
|
||||
const record = data?.['updateCoreViewGroup'];
|
||||
|
||||
if (!record) return;
|
||||
|
||||
apolloClient.cache.modify({
|
||||
id: apolloClient.cache.identify({
|
||||
__typename: 'CoreViewGroup',
|
||||
id: record.id,
|
||||
}),
|
||||
fields: {
|
||||
isVisible: () => record.isVisible,
|
||||
position: () => record.position,
|
||||
},
|
||||
});
|
||||
});
|
||||
},
|
||||
[apolloClient],
|
||||
);
|
||||
|
||||
const deleteCoreViewGroupRecords = useCallback(
|
||||
async (viewGroupsToDelete: ViewGroup[]) => {
|
||||
if (!viewGroupsToDelete.length) return;
|
||||
|
||||
return Promise.all(
|
||||
viewGroupsToDelete.map((viewGroup) =>
|
||||
apolloClient.mutate({
|
||||
mutation: DESTROY_CORE_VIEW_GROUP,
|
||||
variables: {
|
||||
idToDestroy: viewGroup.id,
|
||||
},
|
||||
}),
|
||||
),
|
||||
);
|
||||
},
|
||||
[apolloClient],
|
||||
);
|
||||
|
||||
return {
|
||||
createViewGroupRecords,
|
||||
updateViewGroupRecords,
|
||||
deleteViewGroupRecords,
|
||||
createViewGroupRecords: isCoreViewEnabled
|
||||
? createCoreViewGroupRecords
|
||||
: createViewGroupRecords,
|
||||
updateViewGroupRecords: isCoreViewEnabled
|
||||
? updateCoreViewGroupRecords
|
||||
: updateViewGroupRecords,
|
||||
deleteViewGroupRecords: isCoreViewEnabled
|
||||
? deleteCoreViewGroupRecords
|
||||
: deleteViewGroupRecords,
|
||||
};
|
||||
};
|
||||
|
||||
+132
-5
@@ -12,11 +12,22 @@ import { useCreateOneRecordMutation } from '@/object-record/hooks/useCreateOneRe
|
||||
import { useDestroyOneRecordMutation } from '@/object-record/hooks/useDestroyOneRecordMutation';
|
||||
import { useObjectPermissions } from '@/object-record/hooks/useObjectPermissions';
|
||||
import { useUpdateOneRecordMutation } from '@/object-record/hooks/useUpdateOneRecordMutation';
|
||||
import { CREATE_CORE_VIEW_SORT } from '@/views/graphql/mutations/createCoreViewSort';
|
||||
import { DESTROY_CORE_VIEW_SORT } from '@/views/graphql/mutations/destroyCoreViewSort';
|
||||
import { UPDATE_CORE_VIEW_SORT } from '@/views/graphql/mutations/updateCoreViewSort';
|
||||
import { type GraphQLView } from '@/views/types/GraphQLView';
|
||||
import { type ViewSort } from '@/views/types/ViewSort';
|
||||
import { convertViewSortDirectionToCore } from '@/views/utils/convertViewSortDirectionToCore';
|
||||
import { useFeatureFlagsMap } from '@/workspace/hooks/useFeatureFlagsMap';
|
||||
import { useApolloClient } from '@apollo/client';
|
||||
import { isNull } from '@sniptt/guards';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
import { type CoreViewSort, FeatureFlagKey } from '~/generated/graphql';
|
||||
|
||||
export const usePersistViewSortRecords = () => {
|
||||
const featureFlags = useFeatureFlagsMap();
|
||||
const isCoreViewEnabled = featureFlags[FeatureFlagKey.IS_CORE_VIEW_ENABLED];
|
||||
|
||||
const { objectMetadataItem } = useObjectMetadataItem({
|
||||
objectNameSingular: CoreObjectNameSingular.ViewSort,
|
||||
});
|
||||
@@ -40,9 +51,9 @@ export const usePersistViewSortRecords = () => {
|
||||
const { objectMetadataItems } = useObjectMetadataItems();
|
||||
const { objectPermissionsByObjectMetadataId } = useObjectPermissions();
|
||||
const apolloCoreClient = useApolloCoreClient();
|
||||
|
||||
const apolloClient = useApolloClient();
|
||||
const createViewSortRecords = useCallback(
|
||||
(viewSortsToCreate: ViewSort[], view: GraphQLView) => {
|
||||
(viewSortsToCreate: ViewSort[], view: Pick<GraphQLView, 'id'>) => {
|
||||
if (!viewSortsToCreate.length) return;
|
||||
return Promise.all(
|
||||
viewSortsToCreate.map((viewSort) =>
|
||||
@@ -165,9 +176,125 @@ export const usePersistViewSortRecords = () => {
|
||||
],
|
||||
);
|
||||
|
||||
const createCoreViewSortRecords = useCallback(
|
||||
(viewSortsToCreate: ViewSort[], view: Pick<GraphQLView, 'id'>) => {
|
||||
if (!viewSortsToCreate.length) return;
|
||||
return Promise.all(
|
||||
viewSortsToCreate.map((viewSort) =>
|
||||
apolloClient.mutate({
|
||||
mutation: CREATE_CORE_VIEW_SORT,
|
||||
variables: {
|
||||
input: {
|
||||
fieldMetadataId: viewSort.fieldMetadataId,
|
||||
viewId: view.id,
|
||||
direction: convertViewSortDirectionToCore(viewSort.direction),
|
||||
} satisfies Partial<CoreViewSort>,
|
||||
},
|
||||
update: (cache, { data }) => {
|
||||
const record = data?.['createCoreViewSort'];
|
||||
if (!isDefined(record)) return;
|
||||
|
||||
triggerCreateRecordsOptimisticEffect({
|
||||
cache,
|
||||
objectMetadataItem,
|
||||
recordsToCreate: [record],
|
||||
objectMetadataItems,
|
||||
objectPermissionsByObjectMetadataId,
|
||||
});
|
||||
},
|
||||
}),
|
||||
),
|
||||
);
|
||||
},
|
||||
[
|
||||
apolloClient,
|
||||
objectMetadataItem,
|
||||
objectMetadataItems,
|
||||
objectPermissionsByObjectMetadataId,
|
||||
],
|
||||
);
|
||||
|
||||
const updateCoreViewSortRecords = useCallback(
|
||||
(viewSortsToUpdate: ViewSort[]) => {
|
||||
if (!viewSortsToUpdate.length) return;
|
||||
return Promise.all(
|
||||
viewSortsToUpdate.map((viewSort) =>
|
||||
apolloClient.mutate({
|
||||
mutation: UPDATE_CORE_VIEW_SORT,
|
||||
variables: {
|
||||
idToUpdate: viewSort.id,
|
||||
input: {
|
||||
direction: convertViewSortDirectionToCore(viewSort.direction),
|
||||
} satisfies Partial<CoreViewSort>,
|
||||
},
|
||||
update: (cache, { data }) => {
|
||||
const record = data?.['updateCoreViewSort'];
|
||||
if (!isDefined(record)) return;
|
||||
|
||||
const cachedRecord = getRecordFromCache<ViewSort>(
|
||||
record.id,
|
||||
cache,
|
||||
);
|
||||
if (isNull(cachedRecord)) return;
|
||||
|
||||
triggerUpdateRecordOptimisticEffect({
|
||||
cache,
|
||||
objectMetadataItem,
|
||||
currentRecord: cachedRecord,
|
||||
updatedRecord: record,
|
||||
objectMetadataItems,
|
||||
});
|
||||
},
|
||||
}),
|
||||
),
|
||||
);
|
||||
},
|
||||
[apolloClient, getRecordFromCache, objectMetadataItem, objectMetadataItems],
|
||||
);
|
||||
|
||||
const deleteCoreViewSortRecords = useCallback(
|
||||
(viewSortIdsToDelete: string[]) => {
|
||||
if (!viewSortIdsToDelete.length) return;
|
||||
return Promise.all(
|
||||
viewSortIdsToDelete.map((viewSortId) =>
|
||||
apolloClient.mutate({
|
||||
mutation: DESTROY_CORE_VIEW_SORT,
|
||||
variables: {
|
||||
idToDestroy: viewSortId,
|
||||
},
|
||||
update: (cache, { data }) => {
|
||||
const record = data?.['destroyCoreViewSort'];
|
||||
if (!isDefined(record)) return;
|
||||
|
||||
const cachedRecord = getRecordFromCache<ViewSort>(
|
||||
record.id,
|
||||
cache,
|
||||
);
|
||||
if (isNull(cachedRecord)) return;
|
||||
|
||||
triggerDestroyRecordsOptimisticEffect({
|
||||
cache,
|
||||
objectMetadataItem,
|
||||
recordsToDestroy: [cachedRecord],
|
||||
objectMetadataItems,
|
||||
});
|
||||
},
|
||||
}),
|
||||
),
|
||||
);
|
||||
},
|
||||
[apolloClient, getRecordFromCache, objectMetadataItem, objectMetadataItems],
|
||||
);
|
||||
|
||||
return {
|
||||
createViewSortRecords,
|
||||
updateViewSortRecords,
|
||||
deleteViewSortRecords,
|
||||
createViewSortRecords: isCoreViewEnabled
|
||||
? createCoreViewSortRecords
|
||||
: createViewSortRecords,
|
||||
updateViewSortRecords: isCoreViewEnabled
|
||||
? updateCoreViewSortRecords
|
||||
: updateViewSortRecords,
|
||||
deleteViewSortRecords: isCoreViewEnabled
|
||||
? deleteCoreViewSortRecords
|
||||
: deleteViewSortRecords,
|
||||
};
|
||||
};
|
||||
|
||||
@@ -22,16 +22,23 @@ import { type View } from '@/views/types/View';
|
||||
import { type ViewGroup } from '@/views/types/ViewGroup';
|
||||
import { type ViewSort } from '@/views/types/ViewSort';
|
||||
import { ViewType } from '@/views/types/ViewType';
|
||||
import { convertViewOpenRecordInToCore } from '@/views/utils/convertViewOpenRecordInToCore';
|
||||
import { convertViewTypeToCore } from '@/views/utils/convertViewTypeToCore';
|
||||
import { duplicateViewFiltersAndViewFilterGroups } from '@/views/utils/duplicateViewFiltersAndViewFilterGroups';
|
||||
import { mapRecordFilterGroupToViewFilterGroup } from '@/views/utils/mapRecordFilterGroupToViewFilterGroup';
|
||||
import { mapRecordFilterToViewFilter } from '@/views/utils/mapRecordFilterToViewFilter';
|
||||
import { mapRecordSortToViewSort } from '@/views/utils/mapRecordSortToViewSort';
|
||||
import { useFeatureFlagsMap } from '@/workspace/hooks/useFeatureFlagsMap';
|
||||
import { useRecoilCallback } from 'recoil';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
import { v4 } from 'uuid';
|
||||
import { FeatureFlagKey, useCreateCoreViewMutation } from '~/generated/graphql';
|
||||
import { isUndefinedOrNull } from '~/utils/isUndefinedOrNull';
|
||||
|
||||
export const useCreateViewFromCurrentView = (viewBarComponentId?: string) => {
|
||||
const featureFlags = useFeatureFlagsMap();
|
||||
const isCoreViewEnabled = featureFlags[FeatureFlagKey.IS_CORE_VIEW_ENABLED];
|
||||
const [createCoreViewMutation] = useCreateCoreViewMutation();
|
||||
const currentViewIdCallbackState = useRecoilComponentCallbackState(
|
||||
contextStoreCurrentViewIdComponentState,
|
||||
viewBarComponentId,
|
||||
@@ -90,14 +97,14 @@ export const useCreateViewFromCurrentView = (viewBarComponentId?: string) => {
|
||||
>
|
||||
>,
|
||||
shouldCopyFiltersAndSortsAndAggregate?: boolean,
|
||||
) => {
|
||||
): Promise<string | undefined> => {
|
||||
const currentViewId = getSnapshotValue(
|
||||
snapshot,
|
||||
currentViewIdCallbackState,
|
||||
);
|
||||
|
||||
if (!isDefined(currentViewId)) {
|
||||
return;
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const sourceView = snapshot
|
||||
@@ -109,36 +116,65 @@ export const useCreateViewFromCurrentView = (viewBarComponentId?: string) => {
|
||||
.getValue();
|
||||
|
||||
if (!isDefined(sourceView)) {
|
||||
return;
|
||||
return undefined;
|
||||
}
|
||||
|
||||
set(isPersistingViewFieldsState, true);
|
||||
|
||||
const newView = await createOneRecord({
|
||||
id: id ?? v4(),
|
||||
name: name ?? sourceView.name,
|
||||
icon: icon ?? sourceView.icon,
|
||||
key: null,
|
||||
kanbanFieldMetadataId:
|
||||
kanbanFieldMetadataId ?? sourceView.kanbanFieldMetadataId,
|
||||
kanbanAggregateOperation: shouldCopyFiltersAndSortsAndAggregate
|
||||
? sourceView.kanbanAggregateOperation
|
||||
: undefined,
|
||||
kanbanAggregateOperationFieldMetadataId:
|
||||
shouldCopyFiltersAndSortsAndAggregate
|
||||
? sourceView.kanbanAggregateOperationFieldMetadataId
|
||||
: undefined,
|
||||
type: type ?? sourceView.type,
|
||||
objectMetadataId: sourceView.objectMetadataId,
|
||||
openRecordIn: sourceView.openRecordIn,
|
||||
anyFieldFilterValue: anyFieldFilterValue,
|
||||
});
|
||||
let newViewId: string | undefined;
|
||||
|
||||
if (isUndefinedOrNull(newView)) {
|
||||
if (isCoreViewEnabled) {
|
||||
const result = await createCoreViewMutation({
|
||||
variables: {
|
||||
input: {
|
||||
name: name ?? sourceView.name,
|
||||
icon: icon ?? sourceView.icon,
|
||||
key: null,
|
||||
kanbanAggregateOperation: shouldCopyFiltersAndSortsAndAggregate
|
||||
? sourceView.kanbanAggregateOperation
|
||||
: undefined,
|
||||
kanbanAggregateOperationFieldMetadataId:
|
||||
shouldCopyFiltersAndSortsAndAggregate
|
||||
? sourceView.kanbanAggregateOperationFieldMetadataId
|
||||
: undefined,
|
||||
type: convertViewTypeToCore(type ?? sourceView.type),
|
||||
objectMetadataId: sourceView.objectMetadataId,
|
||||
openRecordIn: convertViewOpenRecordInToCore(
|
||||
sourceView.openRecordIn,
|
||||
),
|
||||
anyFieldFilterValue: anyFieldFilterValue,
|
||||
},
|
||||
},
|
||||
});
|
||||
newViewId = result.data?.createCoreView?.id ?? undefined;
|
||||
} else {
|
||||
const createdView = await createOneRecord({
|
||||
id: id ?? v4(),
|
||||
name: name ?? sourceView.name,
|
||||
icon: icon ?? sourceView.icon,
|
||||
key: null,
|
||||
kanbanFieldMetadataId:
|
||||
kanbanFieldMetadataId ?? sourceView.kanbanFieldMetadataId,
|
||||
kanbanAggregateOperation: shouldCopyFiltersAndSortsAndAggregate
|
||||
? sourceView.kanbanAggregateOperation
|
||||
: undefined,
|
||||
kanbanAggregateOperationFieldMetadataId:
|
||||
shouldCopyFiltersAndSortsAndAggregate
|
||||
? sourceView.kanbanAggregateOperationFieldMetadataId
|
||||
: undefined,
|
||||
type: type ?? sourceView.type,
|
||||
objectMetadataId: sourceView.objectMetadataId,
|
||||
openRecordIn: sourceView.openRecordIn,
|
||||
anyFieldFilterValue: anyFieldFilterValue,
|
||||
});
|
||||
newViewId = createdView.id;
|
||||
}
|
||||
|
||||
if (isUndefinedOrNull(newViewId)) {
|
||||
throw new Error('Failed to create view');
|
||||
}
|
||||
|
||||
await createViewFieldRecords(sourceView.viewFields, newView);
|
||||
await createViewFieldRecords(sourceView.viewFields, { id: newViewId });
|
||||
|
||||
if (type === ViewType.Kanban) {
|
||||
if (!isDefined(kanbanFieldMetadataId)) {
|
||||
@@ -171,7 +207,7 @@ export const useCreateViewFromCurrentView = (viewBarComponentId?: string) => {
|
||||
|
||||
await createViewGroupRecords({
|
||||
viewGroupsToCreate,
|
||||
viewId: newView.id,
|
||||
viewId: newViewId,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -180,7 +216,7 @@ export const useCreateViewFromCurrentView = (viewBarComponentId?: string) => {
|
||||
(recordFilterGroup) =>
|
||||
mapRecordFilterGroupToViewFilterGroup({
|
||||
recordFilterGroup,
|
||||
view: newView,
|
||||
view: { id: newViewId },
|
||||
}),
|
||||
);
|
||||
|
||||
@@ -206,13 +242,16 @@ export const useCreateViewFromCurrentView = (viewBarComponentId?: string) => {
|
||||
}) satisfies ViewSort,
|
||||
);
|
||||
|
||||
await createViewFilterGroupRecords(viewFilterGroupsToCreate, newView);
|
||||
await createViewFilterRecords(viewFiltersToCreate, newView);
|
||||
await createViewSortRecords(viewSortsToCreate, newView);
|
||||
await createViewFilterGroupRecords(viewFilterGroupsToCreate, {
|
||||
id: newViewId,
|
||||
});
|
||||
await createViewFilterRecords(viewFiltersToCreate, { id: newViewId });
|
||||
await createViewSortRecords(viewSortsToCreate, { id: newViewId });
|
||||
}
|
||||
|
||||
await findManyRecordsLazy();
|
||||
set(isPersistingViewFieldsState, false);
|
||||
return newViewId;
|
||||
},
|
||||
[
|
||||
anyFieldFilterValue,
|
||||
@@ -228,6 +267,8 @@ export const useCreateViewFromCurrentView = (viewBarComponentId?: string) => {
|
||||
currentRecordFilters,
|
||||
currentRecordSorts,
|
||||
currentRecordFilterGroups,
|
||||
isCoreViewEnabled,
|
||||
createCoreViewMutation,
|
||||
],
|
||||
);
|
||||
|
||||
|
||||
@@ -5,17 +5,26 @@ import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSi
|
||||
import { useUpdateOneRecord } from '@/object-record/hooks/useUpdateOneRecord';
|
||||
import { useRecoilComponentCallbackState } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentCallbackState';
|
||||
import { type GraphQLView } from '@/views/types/GraphQLView';
|
||||
import { convertUpdateViewInputToCore } from '@/views/utils/convertUpdateViewInputToCore';
|
||||
import { useFeatureFlagsMap } from '@/workspace/hooks/useFeatureFlagsMap';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
import { useUpdateCoreViewMutation } from '~/generated-metadata/graphql';
|
||||
import { FeatureFlagKey } from '~/generated/graphql';
|
||||
|
||||
export const useUpdateCurrentView = () => {
|
||||
const currentViewIdCallbackState = useRecoilComponentCallbackState(
|
||||
contextStoreCurrentViewIdComponentState,
|
||||
);
|
||||
|
||||
const featureFlagMap = useFeatureFlagsMap();
|
||||
const isCoreViewEnabled = featureFlagMap[FeatureFlagKey.IS_CORE_VIEW_ENABLED];
|
||||
|
||||
const { updateOneRecord } = useUpdateOneRecord({
|
||||
objectNameSingular: CoreObjectNameSingular.View,
|
||||
});
|
||||
|
||||
const [updateOneCoreView] = useUpdateCoreViewMutation();
|
||||
|
||||
const updateCurrentView = useRecoilCallback(
|
||||
({ snapshot }) =>
|
||||
async (view: Partial<GraphQLView>) => {
|
||||
@@ -24,13 +33,29 @@ export const useUpdateCurrentView = () => {
|
||||
.getValue();
|
||||
|
||||
if (isDefined(currentViewId)) {
|
||||
await updateOneRecord({
|
||||
idToUpdate: currentViewId,
|
||||
updateOneRecordInput: view,
|
||||
});
|
||||
if (isCoreViewEnabled) {
|
||||
const input = convertUpdateViewInputToCore(view);
|
||||
|
||||
await updateOneCoreView({
|
||||
variables: {
|
||||
id: currentViewId,
|
||||
input,
|
||||
},
|
||||
});
|
||||
} else {
|
||||
await updateOneRecord({
|
||||
idToUpdate: currentViewId,
|
||||
updateOneRecordInput: view,
|
||||
});
|
||||
}
|
||||
}
|
||||
},
|
||||
[currentViewIdCallbackState, updateOneRecord],
|
||||
[
|
||||
currentViewIdCallbackState,
|
||||
isCoreViewEnabled,
|
||||
updateOneCoreView,
|
||||
updateOneRecord,
|
||||
],
|
||||
);
|
||||
|
||||
return {
|
||||
|
||||
@@ -1,24 +1,41 @@
|
||||
import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular';
|
||||
import { useUpdateOneRecord } from '@/object-record/hooks/useUpdateOneRecord';
|
||||
import { type GraphQLView } from '@/views/types/GraphQLView';
|
||||
import { convertUpdateViewInputToCore } from '@/views/utils/convertUpdateViewInputToCore';
|
||||
import { useFeatureFlagsMap } from '@/workspace/hooks/useFeatureFlagsMap';
|
||||
import { useRecoilCallback } from 'recoil';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
import { FeatureFlagKey, useUpdateCoreViewMutation } from '~/generated/graphql';
|
||||
|
||||
export const useUpdateView = () => {
|
||||
const featureFlagMap = useFeatureFlagsMap();
|
||||
const isCoreViewEnabled = featureFlagMap[FeatureFlagKey.IS_CORE_VIEW_ENABLED];
|
||||
|
||||
const { updateOneRecord } = useUpdateOneRecord({
|
||||
objectNameSingular: CoreObjectNameSingular.View,
|
||||
});
|
||||
|
||||
const [updateOneCoreView] = useUpdateCoreViewMutation();
|
||||
|
||||
const updateView = useRecoilCallback(
|
||||
() => async (view: Partial<GraphQLView>) => {
|
||||
if (isDefined(view.id)) {
|
||||
await updateOneRecord({
|
||||
idToUpdate: view.id,
|
||||
updateOneRecordInput: view,
|
||||
});
|
||||
if (isCoreViewEnabled) {
|
||||
await updateOneCoreView({
|
||||
variables: {
|
||||
id: view.id,
|
||||
input: convertUpdateViewInputToCore(view),
|
||||
},
|
||||
});
|
||||
} else {
|
||||
await updateOneRecord({
|
||||
idToUpdate: view.id,
|
||||
updateOneRecordInput: view,
|
||||
});
|
||||
}
|
||||
}
|
||||
},
|
||||
[updateOneRecord],
|
||||
[isCoreViewEnabled, updateOneCoreView, updateOneRecord],
|
||||
);
|
||||
|
||||
return {
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
import {
|
||||
type AggregateOperations,
|
||||
type CoreViewField,
|
||||
type CoreViewFilter,
|
||||
type CoreViewFilterGroup,
|
||||
type CoreViewGroup,
|
||||
type CoreViewSort,
|
||||
type ViewKey,
|
||||
type ViewOpenRecordIn,
|
||||
type ViewType,
|
||||
} from '~/generated-metadata/graphql';
|
||||
|
||||
export type CoreViewWithRelations = {
|
||||
id: string;
|
||||
name: string;
|
||||
type: ViewType;
|
||||
key?: ViewKey | null;
|
||||
objectMetadataId: string;
|
||||
isCompact: boolean;
|
||||
viewFields: Omit<CoreViewField, 'workspaceId'>[];
|
||||
viewGroups: Omit<CoreViewGroup, 'workspaceId'>[];
|
||||
viewFilters: Omit<CoreViewFilter, 'workspaceId'>[];
|
||||
viewFilterGroups?: Omit<CoreViewFilterGroup, 'workspaceId'>[];
|
||||
viewSorts: Omit<CoreViewSort, 'workspaceId'>[];
|
||||
kanbanAggregateOperation?: AggregateOperations | null;
|
||||
kanbanAggregateOperationFieldMetadataId?: string | null;
|
||||
position: number;
|
||||
icon: string;
|
||||
openRecordIn: ViewOpenRecordIn;
|
||||
anyFieldFilterValue?: string | null;
|
||||
__typename?: 'CoreView';
|
||||
};
|
||||
@@ -0,0 +1,22 @@
|
||||
import { type FieldMetadata } from '@/object-record/record-field/ui/types/FieldMetadata';
|
||||
import { type ColumnDefinition } from '@/object-record/record-table/types/ColumnDefinition';
|
||||
import { type ViewField } from '@/views/types/ViewField';
|
||||
import { type CoreViewField } from '~/generated/graphql';
|
||||
|
||||
export const convertCoreViewFieldToViewField = (
|
||||
coreViewField: Omit<CoreViewField, 'workspaceId'>,
|
||||
): ViewField => {
|
||||
const viewField: ViewField = {
|
||||
__typename: 'ViewField',
|
||||
id: coreViewField.id,
|
||||
fieldMetadataId: coreViewField.fieldMetadataId,
|
||||
position: coreViewField.position,
|
||||
isVisible: coreViewField.isVisible,
|
||||
size: coreViewField.size,
|
||||
aggregateOperation: coreViewField.aggregateOperation ?? null,
|
||||
// TODO: remove this once we have refactored the view field definition
|
||||
definition: undefined as unknown as ColumnDefinition<FieldMetadata>,
|
||||
};
|
||||
|
||||
return viewField;
|
||||
};
|
||||
+21
@@ -0,0 +1,21 @@
|
||||
import { type ViewFilterGroup } from '@/views/types/ViewFilterGroup';
|
||||
import { ViewFilterGroupLogicalOperator } from '@/views/types/ViewFilterGroupLogicalOperator';
|
||||
import { type CoreViewFilterGroup } from '~/generated/graphql';
|
||||
|
||||
export const convertCoreViewFilterGroupToViewFilterGroup = (
|
||||
coreViewFilterGroup: Omit<CoreViewFilterGroup, 'workspaceId'>,
|
||||
): ViewFilterGroup => {
|
||||
return {
|
||||
__typename: 'ViewFilterGroup',
|
||||
id: coreViewFilterGroup.id,
|
||||
viewId: coreViewFilterGroup.viewId,
|
||||
parentViewFilterGroupId:
|
||||
coreViewFilterGroup.parentViewFilterGroupId ?? null,
|
||||
logicalOperator:
|
||||
coreViewFilterGroup.logicalOperator === 'AND'
|
||||
? ViewFilterGroupLogicalOperator.AND
|
||||
: ViewFilterGroupLogicalOperator.OR,
|
||||
positionInViewFilterGroup:
|
||||
coreViewFilterGroup.positionInViewFilterGroup ?? null,
|
||||
};
|
||||
};
|
||||
@@ -0,0 +1,28 @@
|
||||
import { type ViewFilter } from '@/views/types/ViewFilter';
|
||||
import { type CoreViewFilter } from '~/generated/graphql';
|
||||
import { convertViewFilterOperandFromCore } from '../utils/convertViewFilterOperandFromCore';
|
||||
|
||||
export const convertCoreViewFilterToViewFilter = (
|
||||
coreViewFilter: Omit<CoreViewFilter, 'workspaceId'>,
|
||||
): ViewFilter => {
|
||||
return {
|
||||
__typename: 'ViewFilter',
|
||||
id: coreViewFilter.id,
|
||||
fieldMetadataId: coreViewFilter.fieldMetadataId,
|
||||
operand: convertViewFilterOperandFromCore(coreViewFilter.operand),
|
||||
value:
|
||||
typeof coreViewFilter.value === 'string'
|
||||
? coreViewFilter.value
|
||||
: JSON.stringify(coreViewFilter.value ?? ''),
|
||||
displayValue:
|
||||
typeof coreViewFilter.value === 'string'
|
||||
? coreViewFilter.value
|
||||
: JSON.stringify(coreViewFilter.value ?? ''),
|
||||
createdAt: coreViewFilter.createdAt,
|
||||
updatedAt: coreViewFilter.updatedAt,
|
||||
viewId: coreViewFilter.viewId,
|
||||
viewFilterGroupId: coreViewFilter.viewFilterGroupId ?? undefined,
|
||||
positionInViewFilterGroup: coreViewFilter.positionInViewFilterGroup ?? null,
|
||||
subFieldName: (coreViewFilter.subFieldName as any) ?? null,
|
||||
};
|
||||
};
|
||||
@@ -0,0 +1,15 @@
|
||||
import { type ViewGroup } from '@/views/types/ViewGroup';
|
||||
import { type CoreViewGroup } from '~/generated/graphql';
|
||||
|
||||
export const convertCoreViewGroupToViewGroup = (
|
||||
coreViewGroup: Omit<CoreViewGroup, 'workspaceId'>,
|
||||
): ViewGroup => {
|
||||
return {
|
||||
__typename: 'ViewGroup',
|
||||
id: coreViewGroup.id,
|
||||
fieldMetadataId: coreViewGroup.fieldMetadataId,
|
||||
isVisible: coreViewGroup.isVisible,
|
||||
fieldValue: coreViewGroup.fieldValue,
|
||||
position: coreViewGroup.position,
|
||||
};
|
||||
};
|
||||
@@ -0,0 +1,8 @@
|
||||
import { ViewKey } from '@/views/types/ViewKey';
|
||||
import { ViewKey as CoreViewKey } from '~/generated-metadata/graphql';
|
||||
|
||||
export const convertCoreViewKeyToViewKey = (
|
||||
coreViewKey: CoreViewKey | null | undefined,
|
||||
): ViewKey | null => {
|
||||
return coreViewKey === CoreViewKey.INDEX ? ViewKey.Index : null;
|
||||
};
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
import { ViewOpenRecordInType } from '@/views/types/ViewOpenRecordInType';
|
||||
import { ViewOpenRecordIn as CoreViewOpenRecordIn } from '~/generated/graphql';
|
||||
|
||||
export const convertCoreViewOpenRecordInToViewOpenRecordIn = (
|
||||
openIn: CoreViewOpenRecordIn,
|
||||
): ViewOpenRecordInType => {
|
||||
return openIn === CoreViewOpenRecordIn.SIDE_PANEL
|
||||
? ViewOpenRecordInType.SIDE_PANEL
|
||||
: ViewOpenRecordInType.RECORD_PAGE;
|
||||
};
|
||||
@@ -0,0 +1,14 @@
|
||||
import { type ViewSort } from '@/views/types/ViewSort';
|
||||
import { type CoreViewSort, ViewSortDirection } from '~/generated/graphql';
|
||||
|
||||
export const convertCoreViewSortToViewSort = (
|
||||
coreViewSort: Omit<CoreViewSort, 'workspaceId'>,
|
||||
): ViewSort => {
|
||||
return {
|
||||
__typename: 'ViewSort',
|
||||
id: coreViewSort.id,
|
||||
fieldMetadataId: coreViewSort.fieldMetadataId,
|
||||
direction:
|
||||
coreViewSort.direction === ViewSortDirection.ASC ? 'asc' : 'desc',
|
||||
};
|
||||
};
|
||||
@@ -0,0 +1,53 @@
|
||||
import { type CoreViewWithRelations } from '@/views/types/CoreViewWithRelations';
|
||||
import { type View } from '@/views/types/View';
|
||||
import { convertCoreViewFieldToViewField } from '@/views/utils/convertCoreViewFieldToViewField';
|
||||
import { convertCoreViewFilterGroupToViewFilterGroup } from '@/views/utils/convertCoreViewFilterGroupToViewFilterGroup';
|
||||
import { convertCoreViewFilterToViewFilter } from '@/views/utils/convertCoreViewFilterToViewFilter';
|
||||
import { convertCoreViewGroupToViewGroup } from '@/views/utils/convertCoreViewGroupToViewGroup';
|
||||
import { convertCoreViewKeyToViewKey } from '@/views/utils/convertCoreViewKeyToViewKey';
|
||||
import { convertCoreViewOpenRecordInToViewOpenRecordIn } from '@/views/utils/convertCoreViewOpenRecordInToViewOpenRecordIn';
|
||||
import { convertCoreViewSortToViewSort } from '@/views/utils/convertCoreViewSortToViewSort';
|
||||
import { convertCoreViewTypeToViewType } from '@/views/utils/convertCoreViewTypeToViewType';
|
||||
|
||||
export const convertCoreViewToView = (
|
||||
coreView: CoreViewWithRelations,
|
||||
): View => {
|
||||
const convertedKey = convertCoreViewKeyToViewKey(coreView.key);
|
||||
const convertedOpenRecordIn = convertCoreViewOpenRecordInToViewOpenRecordIn(
|
||||
coreView.openRecordIn,
|
||||
);
|
||||
const convertedType = convertCoreViewTypeToViewType(coreView.type);
|
||||
|
||||
return {
|
||||
id: coreView.id,
|
||||
name: coreView.name,
|
||||
type: convertedType,
|
||||
key: convertedKey,
|
||||
objectMetadataId: coreView.objectMetadataId,
|
||||
isCompact: coreView.isCompact,
|
||||
viewFields: coreView.viewFields.map((viewField) =>
|
||||
convertCoreViewFieldToViewField(viewField),
|
||||
),
|
||||
viewGroups: coreView.viewGroups.map((viewGroup) =>
|
||||
convertCoreViewGroupToViewGroup(viewGroup),
|
||||
),
|
||||
viewFilters: coreView.viewFilters.map((viewFilter) =>
|
||||
convertCoreViewFilterToViewFilter(viewFilter),
|
||||
),
|
||||
viewFilterGroups: coreView.viewFilterGroups?.map(
|
||||
convertCoreViewFilterGroupToViewFilterGroup,
|
||||
),
|
||||
viewSorts: coreView.viewSorts.map((viewSort) =>
|
||||
convertCoreViewSortToViewSort(viewSort),
|
||||
),
|
||||
kanbanFieldMetadataId: '',
|
||||
kanbanAggregateOperation: coreView.kanbanAggregateOperation ?? null,
|
||||
kanbanAggregateOperationFieldMetadataId:
|
||||
coreView.kanbanAggregateOperationFieldMetadataId ?? null,
|
||||
position: coreView.position,
|
||||
icon: coreView.icon,
|
||||
openRecordIn: convertedOpenRecordIn,
|
||||
anyFieldFilterValue: coreView.anyFieldFilterValue ?? null,
|
||||
__typename: 'View',
|
||||
};
|
||||
};
|
||||
@@ -0,0 +1,10 @@
|
||||
import { ViewType } from '@/views/types/ViewType';
|
||||
import { ViewType as CoreViewType } from '~/generated/graphql';
|
||||
|
||||
export const convertCoreViewTypeToViewType = (
|
||||
coreViewType: CoreViewType,
|
||||
): ViewType => {
|
||||
return coreViewType === CoreViewType.KANBAN
|
||||
? ViewType.Kanban
|
||||
: ViewType.Table;
|
||||
};
|
||||
@@ -0,0 +1,37 @@
|
||||
import { type GraphQLView } from '@/views/types/GraphQLView';
|
||||
import { convertViewKeyToCore } from '@/views/utils/convertViewKeyToCore';
|
||||
import { convertViewOpenRecordInToCore } from '@/views/utils/convertViewOpenRecordInToCore';
|
||||
import { convertViewTypeToCore } from '@/views/utils/convertViewTypeToCore';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
import { type UpdateViewInput } from '~/generated-metadata/graphql';
|
||||
|
||||
export const convertUpdateViewInputToCore = (
|
||||
view: Partial<GraphQLView>,
|
||||
): UpdateViewInput => {
|
||||
const {
|
||||
key,
|
||||
openRecordIn,
|
||||
type,
|
||||
viewFields: _viewFields,
|
||||
viewFilters: _viewFilters,
|
||||
viewFilterGroups: _viewFilterGroups,
|
||||
viewGroups: _viewGroups,
|
||||
viewSorts: _viewSorts,
|
||||
...rest
|
||||
} = view;
|
||||
|
||||
const convertedKey = isDefined(key) ? convertViewKeyToCore(key) : undefined;
|
||||
const convertedOpenRecordIn = isDefined(openRecordIn)
|
||||
? convertViewOpenRecordInToCore(openRecordIn)
|
||||
: undefined;
|
||||
const convertedType = isDefined(type)
|
||||
? convertViewTypeToCore(type)
|
||||
: undefined;
|
||||
|
||||
return {
|
||||
...rest,
|
||||
...(convertedKey && { key: convertedKey }),
|
||||
...(convertedOpenRecordIn && { openRecordIn: convertedOpenRecordIn }),
|
||||
...(convertedType && { type: convertedType }),
|
||||
};
|
||||
};
|
||||
@@ -0,0 +1,26 @@
|
||||
import { ViewFilterOperand } from 'twenty-shared/types';
|
||||
import { ViewFilterOperand as CoreViewFilterOperand } from '~/generated/graphql';
|
||||
|
||||
const mappingFromCore: Record<CoreViewFilterOperand, ViewFilterOperand> = {
|
||||
[CoreViewFilterOperand.IS]: ViewFilterOperand.Is,
|
||||
[CoreViewFilterOperand.IS_NOT_NULL]: ViewFilterOperand.IsNotNull,
|
||||
[CoreViewFilterOperand.IS_NOT]: ViewFilterOperand.IsNot,
|
||||
[CoreViewFilterOperand.LESS_THAN_OR_EQUAL]: ViewFilterOperand.LessThanOrEqual,
|
||||
[CoreViewFilterOperand.GREATER_THAN_OR_EQUAL]:
|
||||
ViewFilterOperand.GreaterThanOrEqual,
|
||||
[CoreViewFilterOperand.IS_BEFORE]: ViewFilterOperand.IsBefore,
|
||||
[CoreViewFilterOperand.IS_AFTER]: ViewFilterOperand.IsAfter,
|
||||
[CoreViewFilterOperand.CONTAINS]: ViewFilterOperand.Contains,
|
||||
[CoreViewFilterOperand.DOES_NOT_CONTAIN]: ViewFilterOperand.DoesNotContain,
|
||||
[CoreViewFilterOperand.IS_EMPTY]: ViewFilterOperand.IsEmpty,
|
||||
[CoreViewFilterOperand.IS_NOT_EMPTY]: ViewFilterOperand.IsNotEmpty,
|
||||
[CoreViewFilterOperand.IS_RELATIVE]: ViewFilterOperand.IsRelative,
|
||||
[CoreViewFilterOperand.IS_IN_PAST]: ViewFilterOperand.IsInPast,
|
||||
[CoreViewFilterOperand.IS_IN_FUTURE]: ViewFilterOperand.IsInFuture,
|
||||
[CoreViewFilterOperand.IS_TODAY]: ViewFilterOperand.IsToday,
|
||||
[CoreViewFilterOperand.VECTOR_SEARCH]: ViewFilterOperand.VectorSearch,
|
||||
};
|
||||
|
||||
export const convertViewFilterOperandFromCore = (
|
||||
coreOperand: CoreViewFilterOperand,
|
||||
): ViewFilterOperand => mappingFromCore[coreOperand];
|
||||
@@ -0,0 +1,28 @@
|
||||
import { ViewFilterOperand } from 'twenty-shared/types';
|
||||
import { ViewFilterOperand as CoreViewFilterOperand } from '~/generated-metadata/graphql';
|
||||
|
||||
const operandMapping: Record<ViewFilterOperand, CoreViewFilterOperand> = {
|
||||
[ViewFilterOperand.Is]: CoreViewFilterOperand.IS,
|
||||
[ViewFilterOperand.IsNotNull]: CoreViewFilterOperand.IS_NOT_NULL,
|
||||
[ViewFilterOperand.IsNot]: CoreViewFilterOperand.IS_NOT,
|
||||
[ViewFilterOperand.LessThanOrEqual]: CoreViewFilterOperand.LESS_THAN_OR_EQUAL,
|
||||
[ViewFilterOperand.GreaterThanOrEqual]:
|
||||
CoreViewFilterOperand.GREATER_THAN_OR_EQUAL,
|
||||
[ViewFilterOperand.IsBefore]: CoreViewFilterOperand.IS_BEFORE,
|
||||
[ViewFilterOperand.IsAfter]: CoreViewFilterOperand.IS_AFTER,
|
||||
[ViewFilterOperand.Contains]: CoreViewFilterOperand.CONTAINS,
|
||||
[ViewFilterOperand.DoesNotContain]: CoreViewFilterOperand.DOES_NOT_CONTAIN,
|
||||
[ViewFilterOperand.IsEmpty]: CoreViewFilterOperand.IS_EMPTY,
|
||||
[ViewFilterOperand.IsNotEmpty]: CoreViewFilterOperand.IS_NOT_EMPTY,
|
||||
[ViewFilterOperand.IsRelative]: CoreViewFilterOperand.IS_RELATIVE,
|
||||
[ViewFilterOperand.IsInPast]: CoreViewFilterOperand.IS_IN_PAST,
|
||||
[ViewFilterOperand.IsInFuture]: CoreViewFilterOperand.IS_IN_FUTURE,
|
||||
[ViewFilterOperand.IsToday]: CoreViewFilterOperand.IS_TODAY,
|
||||
[ViewFilterOperand.VectorSearch]: CoreViewFilterOperand.VECTOR_SEARCH,
|
||||
};
|
||||
|
||||
export const convertViewFilterOperandToCore = (
|
||||
sharedOperand: ViewFilterOperand,
|
||||
): CoreViewFilterOperand => {
|
||||
return operandMapping[sharedOperand];
|
||||
};
|
||||
@@ -0,0 +1,7 @@
|
||||
import { ViewKey } from '~/generated-metadata/graphql';
|
||||
|
||||
export const convertViewKeyToCore = (
|
||||
viewKey: string | null | undefined,
|
||||
): ViewKey | null => {
|
||||
return viewKey === 'INDEX' ? ViewKey.INDEX : null;
|
||||
};
|
||||
@@ -0,0 +1,9 @@
|
||||
import { ViewOpenRecordIn } from '~/generated-metadata/graphql';
|
||||
|
||||
export const convertViewOpenRecordInToCore = (
|
||||
viewOpenRecordIn: string,
|
||||
): ViewOpenRecordIn => {
|
||||
return viewOpenRecordIn === ViewOpenRecordIn.SIDE_PANEL
|
||||
? ViewOpenRecordIn.SIDE_PANEL
|
||||
: ViewOpenRecordIn.RECORD_PAGE;
|
||||
};
|
||||
@@ -0,0 +1,9 @@
|
||||
import { ViewSortDirection } from '~/generated/graphql';
|
||||
|
||||
export const convertViewSortDirectionToCore = (
|
||||
viewSortDirection: string,
|
||||
): ViewSortDirection => {
|
||||
return viewSortDirection === 'asc'
|
||||
? ViewSortDirection.ASC
|
||||
: ViewSortDirection.DESC;
|
||||
};
|
||||
@@ -0,0 +1,5 @@
|
||||
import { ViewType } from '~/generated-metadata/graphql';
|
||||
|
||||
export const convertViewTypeToCore = (viewType: string): ViewType => {
|
||||
return viewType === 'kanban' ? ViewType.KANBAN : ViewType.TABLE;
|
||||
};
|
||||
+1
-1
@@ -85,7 +85,7 @@ export const ViewPickerListContent = () => {
|
||||
Promise.all(
|
||||
viewsReordered.map(async (view, index) => {
|
||||
if (view.position !== index) {
|
||||
await updateView({ ...view, position: index });
|
||||
await updateView({ id: view.id, position: index });
|
||||
}
|
||||
}),
|
||||
);
|
||||
|
||||
-5
@@ -46,11 +46,6 @@ describe('shouldDisplayRawJsonByDefault', () => {
|
||||
const defaultValue = '{"key1": "value1", "key2":}';
|
||||
expect(shouldDisplayRawJsonByDefault(defaultValue)).toBe(true);
|
||||
});
|
||||
|
||||
it('should return true for plain text', () => {
|
||||
const defaultValue = 'This is just plain text';
|
||||
expect(shouldDisplayRawJsonByDefault(defaultValue)).toBe(true);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
+8
-4
@@ -2,7 +2,7 @@ import { InjectDataSource, InjectRepository } from '@nestjs/typeorm';
|
||||
|
||||
import { Command } from 'nest-commander';
|
||||
import { type ViewFilterOperand as SharedViewFilterOperand } from 'twenty-shared/types';
|
||||
import { DataSource, type QueryRunner, Repository } from 'typeorm';
|
||||
import { DataSource, Repository, type QueryRunner } from 'typeorm';
|
||||
|
||||
import {
|
||||
ActiveOrSuspendedWorkspacesMigrationCommandRunner,
|
||||
@@ -17,6 +17,7 @@ import { ViewGroup } from 'src/engine/core-modules/view/entities/view-group.enti
|
||||
import { ViewSort } from 'src/engine/core-modules/view/entities/view-sort.entity';
|
||||
import { View } from 'src/engine/core-modules/view/entities/view.entity';
|
||||
import { type ViewFilterGroupLogicalOperator } from 'src/engine/core-modules/view/enums/view-filter-group-logical-operator';
|
||||
import { ViewKey } from 'src/engine/core-modules/view/enums/view-key.enum';
|
||||
import { ViewOpenRecordIn } from 'src/engine/core-modules/view/enums/view-open-record-in';
|
||||
import { type ViewSortDirection } from 'src/engine/core-modules/view/enums/view-sort-direction';
|
||||
import { ViewType } from 'src/engine/core-modules/view/enums/view-type.enum';
|
||||
@@ -29,7 +30,7 @@ import { type ViewGroupWorkspaceEntity } from 'src/modules/view/standard-objects
|
||||
import { type ViewSortWorkspaceEntity } from 'src/modules/view/standard-objects/view-sort.workspace-entity';
|
||||
import { type ViewWorkspaceEntity } from 'src/modules/view/standard-objects/view.workspace-entity';
|
||||
import { convertViewFilterOperandToCoreOperand } from 'src/modules/view/utils/convert-view-filter-operand-to-core-operand.util';
|
||||
import { transformViewFilterWorkspaceValueToCoreValue } from 'src/modules/view/utils/transform-view-filter-workspace-value-to-core-value';
|
||||
import { convertViewFilterWorkspaceValueToCoreValue } from 'src/modules/view/utils/convert-view-filter-workspace-value-to-core-value';
|
||||
|
||||
@Command({
|
||||
name: 'migrate:views-to-core',
|
||||
@@ -257,7 +258,10 @@ export class MigrateViewsToCoreCommand extends ActiveOrSuspendedWorkspacesMigrat
|
||||
name: viewName,
|
||||
objectMetadataId: workspaceView.objectMetadataId,
|
||||
type: workspaceView.type === 'table' ? ViewType.TABLE : ViewType.KANBAN,
|
||||
key: workspaceView.key,
|
||||
key:
|
||||
workspaceView.key === 'INDEX' || workspaceView.key === ViewKey.INDEX
|
||||
? ViewKey.INDEX
|
||||
: null,
|
||||
icon: workspaceView.icon,
|
||||
position: workspaceView.position,
|
||||
isCompact: workspaceView.isCompact,
|
||||
@@ -328,7 +332,7 @@ export class MigrateViewsToCoreCommand extends ActiveOrSuspendedWorkspacesMigrat
|
||||
operand: convertViewFilterOperandToCoreOperand(
|
||||
filter.operand as SharedViewFilterOperand,
|
||||
),
|
||||
value: transformViewFilterWorkspaceValueToCoreValue(filter.value),
|
||||
value: convertViewFilterWorkspaceValueToCoreValue(filter.value),
|
||||
viewFilterGroupId: filter.viewFilterGroupId,
|
||||
workspaceId,
|
||||
createdAt: new Date(filter.createdAt),
|
||||
|
||||
+25
@@ -0,0 +1,25 @@
|
||||
import { type MigrationInterface, type QueryRunner } from 'typeorm';
|
||||
|
||||
export class MigrateViewKeyFromStringToEnum1755189372929
|
||||
implements MigrationInterface
|
||||
{
|
||||
name = 'MigrateViewKeyFromStringToEnum1755189372929';
|
||||
|
||||
public async up(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query(`ALTER TABLE "core"."view" DROP COLUMN "key"`);
|
||||
await queryRunner.query(
|
||||
`CREATE TYPE "core"."view_key_enum" AS ENUM('INDEX')`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "core"."view" ADD "key" "core"."view_key_enum"`,
|
||||
);
|
||||
}
|
||||
|
||||
public async down(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query(`ALTER TABLE "core"."view" DROP COLUMN "key"`);
|
||||
await queryRunner.query(`DROP TYPE "core"."view_key_enum"`);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "core"."view" ADD "key" text DEFAULT 'INDEX'`,
|
||||
);
|
||||
}
|
||||
}
|
||||
-2
@@ -376,8 +376,6 @@ export class ObjectRecordsToGraphqlConnectionHelper {
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
private formatFieldValue(value: any, fieldType: FieldMetadataType) {
|
||||
switch (fieldType) {
|
||||
case FieldMetadataType.RAW_JSON:
|
||||
return value ? JSON.stringify(value) : value;
|
||||
case FieldMetadataType.DATE:
|
||||
case FieldMetadataType.DATE_TIME:
|
||||
return value instanceof Date ? value.toISOString() : value;
|
||||
|
||||
-3
@@ -3,7 +3,6 @@ import { BigIntScalarType } from './big-int.scalar';
|
||||
import { CursorScalarType } from './cursor.scalar';
|
||||
import { DateScalarType } from './date.scalar';
|
||||
import { PositionScalarType } from './position.scalar';
|
||||
import { RawJSONScalar } from './raw-json.scalar';
|
||||
import { TimeScalarType } from './time.scalar';
|
||||
import { TSVectorScalarType } from './ts-vector.scalar';
|
||||
import { UUIDScalarType } from './uuid.scalar';
|
||||
@@ -13,7 +12,6 @@ export * from './big-int.scalar';
|
||||
export * from './cursor.scalar';
|
||||
export * from './date.scalar';
|
||||
export * from './position.scalar';
|
||||
export * from './raw-json.scalar';
|
||||
export * from './time.scalar';
|
||||
export * from './ts-vector.scalar';
|
||||
export * from './uuid.scalar';
|
||||
@@ -26,6 +24,5 @@ export const scalars = [
|
||||
UUIDScalarType,
|
||||
CursorScalarType,
|
||||
PositionScalarType,
|
||||
RawJSONScalar,
|
||||
TSVectorScalarType,
|
||||
];
|
||||
|
||||
-81
@@ -1,81 +0,0 @@
|
||||
import { GraphQLScalarType } from 'graphql';
|
||||
import { type Maybe } from 'graphql-yoga';
|
||||
import { type ObjMap } from 'graphql/jsutils/ObjMap';
|
||||
import { type ASTNode, Kind, type ValueNode } from 'graphql/language';
|
||||
|
||||
import { ValidationError } from 'src/engine/core-modules/graphql/utils/graphql-errors.util';
|
||||
|
||||
const parseLiteral = (
|
||||
ast: ValueNode,
|
||||
variables?: Maybe<ObjMap<unknown>>,
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
): any => {
|
||||
switch (ast.kind) {
|
||||
case Kind.STRING:
|
||||
case Kind.BOOLEAN:
|
||||
return ast.value;
|
||||
case Kind.INT:
|
||||
case Kind.FLOAT:
|
||||
return parseFloat(ast.value);
|
||||
case Kind.OBJECT:
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
return parseObject(ast as any, variables);
|
||||
case Kind.LIST:
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
return (ast as any).values.map((n: ValueNode) =>
|
||||
parseLiteral(n, variables),
|
||||
);
|
||||
case Kind.NULL:
|
||||
return null;
|
||||
case Kind.VARIABLE:
|
||||
return variables ? variables[ast.name.value] : undefined;
|
||||
default:
|
||||
throw new ValidationError(
|
||||
`JSONStringify cannot represent value: ${JSON.stringify(ast)}`,
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
const parseObject = (
|
||||
ast: ASTNode,
|
||||
variables?: Maybe<ObjMap<unknown>>,
|
||||
): object => {
|
||||
const value = Object.create(null);
|
||||
|
||||
if ('fields' in ast) {
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
ast.fields?.forEach((field: any) => {
|
||||
value[field.name.value] = parseLiteral(field.value, variables);
|
||||
});
|
||||
}
|
||||
|
||||
return value;
|
||||
};
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
const stringify = (value: any): string => {
|
||||
return JSON.stringify(value);
|
||||
};
|
||||
|
||||
const parseJSON = (value: string): object => {
|
||||
try {
|
||||
return JSON.parse(value);
|
||||
} catch {
|
||||
throw new ValidationError(`Value is not valid JSON: ${value}`);
|
||||
}
|
||||
};
|
||||
|
||||
export const RawJSONScalar = new GraphQLScalarType({
|
||||
name: 'RawJSONScalar',
|
||||
description:
|
||||
'The `RawJSONScalar` scalar type represents JSON values, but stringifies inputs and parses outputs.',
|
||||
serialize: parseJSON,
|
||||
parseValue: stringify,
|
||||
parseLiteral: (ast, variables) => {
|
||||
if (ast.kind === Kind.STRING) {
|
||||
return stringify(ast.value);
|
||||
} else {
|
||||
return stringify(parseLiteral(ast, variables));
|
||||
}
|
||||
},
|
||||
});
|
||||
+2
-2
@@ -13,6 +13,7 @@ import {
|
||||
GraphQLString,
|
||||
type GraphQLType,
|
||||
} from 'graphql';
|
||||
import GraphQLJSON from 'graphql-type-json';
|
||||
import { FieldMetadataType } from 'twenty-shared/types';
|
||||
|
||||
import {
|
||||
@@ -41,7 +42,6 @@ import {
|
||||
UUIDScalarType,
|
||||
} from 'src/engine/api/graphql/workspace-schema-builder/graphql-types/scalars';
|
||||
import { PositionScalarType } from 'src/engine/api/graphql/workspace-schema-builder/graphql-types/scalars/position.scalar';
|
||||
import { RawJSONScalar } from 'src/engine/api/graphql/workspace-schema-builder/graphql-types/scalars/raw-json.scalar';
|
||||
import { getNumberFilterType } from 'src/engine/api/graphql/workspace-schema-builder/utils/get-number-filter-type.util';
|
||||
import { getNumberScalarType } from 'src/engine/api/graphql/workspace-schema-builder/utils/get-number-scalar-type.util';
|
||||
|
||||
@@ -87,7 +87,7 @@ export class TypeMapperService {
|
||||
],
|
||||
[FieldMetadataType.NUMERIC, BigFloatScalarType],
|
||||
[FieldMetadataType.POSITION, PositionScalarType],
|
||||
[FieldMetadataType.RAW_JSON, RawJSONScalar],
|
||||
[FieldMetadataType.RAW_JSON, GraphQLJSON],
|
||||
[
|
||||
FieldMetadataType.ARRAY,
|
||||
StringArrayScalarType as unknown as GraphQLScalarType,
|
||||
|
||||
+6
-10
@@ -1,5 +1,5 @@
|
||||
import { t } from '@lingui/core/macro';
|
||||
import { isNonEmptyString } from '@sniptt/guards';
|
||||
import { isArray, isNonEmptyString } from '@sniptt/guards';
|
||||
import {
|
||||
type CountryCallingCode,
|
||||
parsePhoneNumberWithError,
|
||||
@@ -8,7 +8,6 @@ import {
|
||||
getCountryCodesForCallingCode,
|
||||
isDefined,
|
||||
isValidCountryCode,
|
||||
parseJson,
|
||||
removeUndefinedFields,
|
||||
} from 'twenty-shared/utils';
|
||||
|
||||
@@ -24,7 +23,7 @@ import {
|
||||
export type PhonesFieldGraphQLInput =
|
||||
| Partial<
|
||||
Omit<PhonesMetadata, 'additionalPhones'> & {
|
||||
additionalPhones: string | null;
|
||||
additionalPhones: Partial<AdditionalPhoneMetadata>[];
|
||||
}
|
||||
>
|
||||
| null
|
||||
@@ -195,15 +194,12 @@ export const transformPhonesValue = ({
|
||||
number: primary.primaryPhoneNumber,
|
||||
});
|
||||
|
||||
const parsedAdditionalPhones = isDefined(additionalPhones)
|
||||
? parseJson<AdditionalPhoneMetadata[]>(additionalPhones)
|
||||
: additionalPhones;
|
||||
const transformedAdditionalPhones = isDefined(parsedAdditionalPhones)
|
||||
? JSON.stringify(parsedAdditionalPhones.map(validateAndInferPhoneInput))
|
||||
: parsedAdditionalPhones;
|
||||
const parsedAdditionalPhones = isArray(additionalPhones)
|
||||
? additionalPhones
|
||||
: [];
|
||||
|
||||
return removeUndefinedFields({
|
||||
additionalPhones: transformedAdditionalPhones,
|
||||
additionalPhones: parsedAdditionalPhones.map(validateAndInferPhoneInput),
|
||||
primaryPhoneCallingCode,
|
||||
primaryPhoneCountryCode,
|
||||
primaryPhoneNumber,
|
||||
|
||||
+1
-2
@@ -20,11 +20,10 @@ import { ViewFieldRestApiExceptionFilter } from 'src/engine/core-modules/view/fi
|
||||
import { ViewFieldService } from 'src/engine/core-modules/view/services/view-field.service';
|
||||
import { Workspace } from 'src/engine/core-modules/workspace/workspace.entity';
|
||||
import { AuthWorkspace } from 'src/engine/decorators/auth/auth-workspace.decorator';
|
||||
import { JwtAuthGuard } from 'src/engine/guards/jwt-auth.guard';
|
||||
import { WorkspaceAuthGuard } from 'src/engine/guards/workspace-auth.guard';
|
||||
|
||||
@Controller('rest/metadata/viewFields')
|
||||
@UseGuards(JwtAuthGuard, WorkspaceAuthGuard)
|
||||
@UseGuards(WorkspaceAuthGuard)
|
||||
@UseFilters(ViewFieldRestApiExceptionFilter)
|
||||
export class ViewFieldController {
|
||||
constructor(private readonly viewFieldService: ViewFieldService) {}
|
||||
|
||||
+1
-2
@@ -20,11 +20,10 @@ import { ViewFilterGroupRestApiExceptionFilter } from 'src/engine/core-modules/v
|
||||
import { ViewFilterGroupService } from 'src/engine/core-modules/view/services/view-filter-group.service';
|
||||
import { Workspace } from 'src/engine/core-modules/workspace/workspace.entity';
|
||||
import { AuthWorkspace } from 'src/engine/decorators/auth/auth-workspace.decorator';
|
||||
import { JwtAuthGuard } from 'src/engine/guards/jwt-auth.guard';
|
||||
import { WorkspaceAuthGuard } from 'src/engine/guards/workspace-auth.guard';
|
||||
|
||||
@Controller('rest/metadata/viewFilterGroups')
|
||||
@UseGuards(JwtAuthGuard, WorkspaceAuthGuard)
|
||||
@UseGuards(WorkspaceAuthGuard)
|
||||
@UseFilters(ViewFilterGroupRestApiExceptionFilter)
|
||||
export class ViewFilterGroupController {
|
||||
constructor(
|
||||
|
||||
+1
-2
@@ -20,11 +20,10 @@ import { ViewFilterRestApiExceptionFilter } from 'src/engine/core-modules/view/f
|
||||
import { ViewFilterService } from 'src/engine/core-modules/view/services/view-filter.service';
|
||||
import { Workspace } from 'src/engine/core-modules/workspace/workspace.entity';
|
||||
import { AuthWorkspace } from 'src/engine/decorators/auth/auth-workspace.decorator';
|
||||
import { JwtAuthGuard } from 'src/engine/guards/jwt-auth.guard';
|
||||
import { WorkspaceAuthGuard } from 'src/engine/guards/workspace-auth.guard';
|
||||
|
||||
@Controller('rest/metadata/viewFilters')
|
||||
@UseGuards(JwtAuthGuard, WorkspaceAuthGuard)
|
||||
@UseGuards(WorkspaceAuthGuard)
|
||||
@UseFilters(ViewFilterRestApiExceptionFilter)
|
||||
export class ViewFilterController {
|
||||
constructor(private readonly viewFilterService: ViewFilterService) {}
|
||||
|
||||
+1
-2
@@ -20,11 +20,10 @@ import { ViewGroupRestApiExceptionFilter } from 'src/engine/core-modules/view/fi
|
||||
import { ViewGroupService } from 'src/engine/core-modules/view/services/view-group.service';
|
||||
import { Workspace } from 'src/engine/core-modules/workspace/workspace.entity';
|
||||
import { AuthWorkspace } from 'src/engine/decorators/auth/auth-workspace.decorator';
|
||||
import { JwtAuthGuard } from 'src/engine/guards/jwt-auth.guard';
|
||||
import { WorkspaceAuthGuard } from 'src/engine/guards/workspace-auth.guard';
|
||||
|
||||
@Controller('rest/metadata/viewGroups')
|
||||
@UseGuards(JwtAuthGuard, WorkspaceAuthGuard)
|
||||
@UseGuards(WorkspaceAuthGuard)
|
||||
@UseFilters(ViewGroupRestApiExceptionFilter)
|
||||
export class ViewGroupController {
|
||||
constructor(private readonly viewGroupService: ViewGroupService) {}
|
||||
|
||||
+1
-2
@@ -20,11 +20,10 @@ import { ViewSortRestApiExceptionFilter } from 'src/engine/core-modules/view/fil
|
||||
import { ViewSortService } from 'src/engine/core-modules/view/services/view-sort.service';
|
||||
import { Workspace } from 'src/engine/core-modules/workspace/workspace.entity';
|
||||
import { AuthWorkspace } from 'src/engine/decorators/auth/auth-workspace.decorator';
|
||||
import { JwtAuthGuard } from 'src/engine/guards/jwt-auth.guard';
|
||||
import { WorkspaceAuthGuard } from 'src/engine/guards/workspace-auth.guard';
|
||||
|
||||
@Controller('rest/metadata/viewSorts')
|
||||
@UseGuards(JwtAuthGuard, WorkspaceAuthGuard)
|
||||
@UseGuards(WorkspaceAuthGuard)
|
||||
@UseFilters(ViewSortRestApiExceptionFilter)
|
||||
export class ViewSortController {
|
||||
constructor(private readonly viewSortService: ViewSortService) {}
|
||||
|
||||
@@ -20,11 +20,10 @@ import { ViewRestApiExceptionFilter } from 'src/engine/core-modules/view/filters
|
||||
import { ViewService } from 'src/engine/core-modules/view/services/view.service';
|
||||
import { Workspace } from 'src/engine/core-modules/workspace/workspace.entity';
|
||||
import { AuthWorkspace } from 'src/engine/decorators/auth/auth-workspace.decorator';
|
||||
import { JwtAuthGuard } from 'src/engine/guards/jwt-auth.guard';
|
||||
import { WorkspaceAuthGuard } from 'src/engine/guards/workspace-auth.guard';
|
||||
|
||||
@Controller('rest/metadata/views')
|
||||
@UseGuards(JwtAuthGuard, WorkspaceAuthGuard)
|
||||
@UseGuards(WorkspaceAuthGuard)
|
||||
@UseFilters(ViewRestApiExceptionFilter)
|
||||
export class ViewController {
|
||||
constructor(private readonly viewService: ViewService) {}
|
||||
|
||||
+3
-2
@@ -1,7 +1,8 @@
|
||||
import { Field, InputType } from '@nestjs/graphql';
|
||||
|
||||
import GraphQLJSON from 'graphql-type-json';
|
||||
|
||||
import { UUIDScalarType } from 'src/engine/api/graphql/workspace-schema-builder/graphql-types/scalars';
|
||||
import { RawJSONScalar } from 'src/engine/api/graphql/workspace-schema-builder/graphql-types/scalars/raw-json.scalar';
|
||||
import { ViewFilterOperand } from 'src/engine/core-modules/view/enums/view-filter-operand';
|
||||
import { ViewFilterValue } from 'src/engine/core-modules/view/types/view-filter-value.type';
|
||||
|
||||
@@ -13,7 +14,7 @@ export class CreateViewFilterInput {
|
||||
@Field({ nullable: true, defaultValue: ViewFilterOperand.CONTAINS })
|
||||
operand?: ViewFilterOperand;
|
||||
|
||||
@Field(() => RawJSONScalar, { nullable: false })
|
||||
@Field(() => GraphQLJSON, { nullable: false })
|
||||
value: ViewFilterValue;
|
||||
|
||||
@Field(() => UUIDScalarType, { nullable: true })
|
||||
|
||||
+3
-2
@@ -2,6 +2,7 @@ import { Field, InputType } from '@nestjs/graphql';
|
||||
|
||||
import { AggregateOperations } from 'src/engine/api/graphql/graphql-query-runner/constants/aggregate-operations.constant';
|
||||
import { UUIDScalarType } from 'src/engine/api/graphql/workspace-schema-builder/graphql-types/scalars';
|
||||
import { ViewKey } from 'src/engine/core-modules/view/enums/view-key.enum';
|
||||
import { ViewOpenRecordIn } from 'src/engine/core-modules/view/enums/view-open-record-in';
|
||||
import { ViewType } from 'src/engine/core-modules/view/enums/view-type.enum';
|
||||
|
||||
@@ -16,8 +17,8 @@ export class CreateViewInput {
|
||||
@Field(() => ViewType, { nullable: true, defaultValue: ViewType.TABLE })
|
||||
type?: ViewType;
|
||||
|
||||
@Field({ nullable: true, defaultValue: 'INDEX' })
|
||||
key?: string;
|
||||
@Field(() => ViewKey, { nullable: true })
|
||||
key?: ViewKey;
|
||||
|
||||
@Field({ nullable: false })
|
||||
icon: string;
|
||||
|
||||
@@ -1,11 +1,9 @@
|
||||
import { Field, ObjectType, registerEnumType } from '@nestjs/graphql';
|
||||
|
||||
import { IDField } from '@ptc-org/nestjs-query-graphql';
|
||||
import GraphQLJSON from 'graphql-type-json';
|
||||
|
||||
import {
|
||||
RawJSONScalar,
|
||||
UUIDScalarType,
|
||||
} from 'src/engine/api/graphql/workspace-schema-builder/graphql-types/scalars';
|
||||
import { UUIDScalarType } from 'src/engine/api/graphql/workspace-schema-builder/graphql-types/scalars';
|
||||
import { ViewFilterOperand } from 'src/engine/core-modules/view/enums/view-filter-operand';
|
||||
import { ViewFilterValue } from 'src/engine/core-modules/view/types/view-filter-value.type';
|
||||
|
||||
@@ -24,7 +22,7 @@ export class ViewFilterDTO {
|
||||
@Field({ nullable: false, defaultValue: ViewFilterOperand.CONTAINS })
|
||||
operand: ViewFilterOperand;
|
||||
|
||||
@Field(() => RawJSONScalar, { nullable: false })
|
||||
@Field(() => GraphQLJSON, { nullable: false })
|
||||
value: ViewFilterValue;
|
||||
|
||||
@Field(() => UUIDScalarType, { nullable: true })
|
||||
|
||||
@@ -4,11 +4,13 @@ import { IDField } from '@ptc-org/nestjs-query-graphql';
|
||||
|
||||
import { AggregateOperations } from 'src/engine/api/graphql/graphql-query-runner/constants/aggregate-operations.constant';
|
||||
import { UUIDScalarType } from 'src/engine/api/graphql/workspace-schema-builder/graphql-types/scalars';
|
||||
import { ViewKey } from 'src/engine/core-modules/view/enums/view-key.enum';
|
||||
import { ViewOpenRecordIn } from 'src/engine/core-modules/view/enums/view-open-record-in';
|
||||
import { ViewType } from 'src/engine/core-modules/view/enums/view-type.enum';
|
||||
|
||||
registerEnumType(ViewOpenRecordIn, { name: 'ViewOpenRecordIn' });
|
||||
registerEnumType(ViewType, { name: 'ViewType' });
|
||||
registerEnumType(ViewKey, { name: 'ViewKey' });
|
||||
|
||||
@ObjectType('CoreView')
|
||||
export class ViewDTO {
|
||||
@@ -24,8 +26,8 @@ export class ViewDTO {
|
||||
@Field(() => ViewType, { nullable: false, defaultValue: ViewType.TABLE })
|
||||
type: ViewType;
|
||||
|
||||
@Field({ nullable: true, defaultValue: 'INDEX' })
|
||||
key: string;
|
||||
@Field(() => ViewKey, { nullable: true, defaultValue: ViewKey.INDEX })
|
||||
key: ViewKey | null;
|
||||
|
||||
@Field({ nullable: false })
|
||||
icon: string;
|
||||
|
||||
@@ -20,6 +20,7 @@ import { ViewFilterGroup } from 'src/engine/core-modules/view/entities/view-filt
|
||||
import { ViewFilter } from 'src/engine/core-modules/view/entities/view-filter.entity';
|
||||
import { ViewGroup } from 'src/engine/core-modules/view/entities/view-group.entity';
|
||||
import { ViewSort } from 'src/engine/core-modules/view/entities/view-sort.entity';
|
||||
import { ViewKey } from 'src/engine/core-modules/view/enums/view-key.enum';
|
||||
import { ViewOpenRecordIn } from 'src/engine/core-modules/view/enums/view-open-record-in';
|
||||
import { ViewType } from 'src/engine/core-modules/view/enums/view-type.enum';
|
||||
import { Workspace } from 'src/engine/core-modules/workspace/workspace.entity';
|
||||
@@ -48,8 +49,13 @@ export class View {
|
||||
})
|
||||
type: ViewType;
|
||||
|
||||
@Column({ nullable: true, type: 'text', default: 'INDEX' })
|
||||
key: string;
|
||||
@Column({
|
||||
type: 'enum',
|
||||
enum: Object.values(ViewKey),
|
||||
nullable: true,
|
||||
default: null,
|
||||
})
|
||||
key: ViewKey | null;
|
||||
|
||||
@Column({ nullable: false, type: 'text' })
|
||||
icon: string;
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
export enum ViewKey {
|
||||
INDEX = 'INDEX',
|
||||
}
|
||||
@@ -16,7 +16,17 @@ import { type I18nContext } from 'src/engine/core-modules/i18n/types/i18n-contex
|
||||
import { generateMessageId } from 'src/engine/core-modules/i18n/utils/generateMessageId';
|
||||
import { CreateViewInput } from 'src/engine/core-modules/view/dtos/inputs/create-view.input';
|
||||
import { UpdateViewInput } from 'src/engine/core-modules/view/dtos/inputs/update-view.input';
|
||||
import { ViewFieldDTO } from 'src/engine/core-modules/view/dtos/view-field.dto';
|
||||
import { ViewFilterGroupDTO } from 'src/engine/core-modules/view/dtos/view-filter-group.dto';
|
||||
import { ViewFilterDTO } from 'src/engine/core-modules/view/dtos/view-filter.dto';
|
||||
import { ViewGroupDTO } from 'src/engine/core-modules/view/dtos/view-group.dto';
|
||||
import { ViewSortDTO } from 'src/engine/core-modules/view/dtos/view-sort.dto';
|
||||
import { ViewDTO } from 'src/engine/core-modules/view/dtos/view.dto';
|
||||
import { ViewFieldService } from 'src/engine/core-modules/view/services/view-field.service';
|
||||
import { ViewFilterGroupService } from 'src/engine/core-modules/view/services/view-filter-group.service';
|
||||
import { ViewFilterService } from 'src/engine/core-modules/view/services/view-filter.service';
|
||||
import { ViewGroupService } from 'src/engine/core-modules/view/services/view-group.service';
|
||||
import { ViewSortService } from 'src/engine/core-modules/view/services/view-sort.service';
|
||||
import { ViewService } from 'src/engine/core-modules/view/services/view.service';
|
||||
import { ViewGraphqlApiExceptionFilter } from 'src/engine/core-modules/view/utils/view-graphql-api-exception.filter';
|
||||
import { Workspace } from 'src/engine/core-modules/workspace/workspace.entity';
|
||||
@@ -30,6 +40,11 @@ import { resolveObjectMetadataStandardOverride } from 'src/engine/metadata-modul
|
||||
export class ViewResolver {
|
||||
constructor(
|
||||
private readonly viewService: ViewService,
|
||||
private readonly viewFieldService: ViewFieldService,
|
||||
private readonly viewFilterService: ViewFilterService,
|
||||
private readonly viewFilterGroupService: ViewFilterGroupService,
|
||||
private readonly viewGroupService: ViewGroupService,
|
||||
private readonly viewSortService: ViewSortService,
|
||||
private readonly i18nService: I18nService,
|
||||
) {}
|
||||
|
||||
@@ -152,4 +167,44 @@ export class ViewResolver {
|
||||
|
||||
return isDefined(deletedView);
|
||||
}
|
||||
|
||||
@ResolveField(() => [ViewFieldDTO])
|
||||
async viewFields(
|
||||
@Parent() view: ViewDTO,
|
||||
@AuthWorkspace() workspace: Workspace,
|
||||
) {
|
||||
return this.viewFieldService.findByViewId(workspace.id, view.id);
|
||||
}
|
||||
|
||||
@ResolveField(() => [ViewFilterDTO])
|
||||
async viewFilters(
|
||||
@Parent() view: ViewDTO,
|
||||
@AuthWorkspace() workspace: Workspace,
|
||||
) {
|
||||
return this.viewFilterService.findByViewId(workspace.id, view.id);
|
||||
}
|
||||
|
||||
@ResolveField(() => [ViewFilterGroupDTO])
|
||||
async viewFilterGroups(
|
||||
@Parent() view: ViewDTO,
|
||||
@AuthWorkspace() workspace: Workspace,
|
||||
) {
|
||||
return this.viewFilterGroupService.findByViewId(workspace.id, view.id);
|
||||
}
|
||||
|
||||
@ResolveField(() => [ViewSortDTO])
|
||||
async viewSorts(
|
||||
@Parent() view: ViewDTO,
|
||||
@AuthWorkspace() workspace: Workspace,
|
||||
) {
|
||||
return this.viewSortService.findByViewId(workspace.id, view.id);
|
||||
}
|
||||
|
||||
@ResolveField(() => [ViewGroupDTO])
|
||||
async viewGroups(
|
||||
@Parent() view: ViewDTO,
|
||||
@AuthWorkspace() workspace: Workspace,
|
||||
) {
|
||||
return this.viewGroupService.findByViewId(workspace.id, view.id);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,6 +6,8 @@ export type RelationFilterValue = {
|
||||
export type ViewFilterValue =
|
||||
| string
|
||||
| string[]
|
||||
| boolean
|
||||
| number
|
||||
| RelationFilterValue
|
||||
| Record<string, unknown>
|
||||
| null
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import { Module } from '@nestjs/common';
|
||||
import { TypeOrmModule } from '@nestjs/typeorm';
|
||||
|
||||
import { AuthModule } from 'src/engine/core-modules/auth/auth.module';
|
||||
import { I18nModule } from 'src/engine/core-modules/i18n/i18n.module';
|
||||
import { ViewFieldController } from 'src/engine/core-modules/view/controllers/view-field.controller';
|
||||
import { ViewFilterGroupController } from 'src/engine/core-modules/view/controllers/view-filter-group.controller';
|
||||
@@ -36,7 +35,6 @@ import { WorkspaceCacheStorageModule } from 'src/engine/workspace-cache-storage/
|
||||
[View, ViewField, ViewFilter, ViewFilterGroup, ViewGroup, ViewSort],
|
||||
'core',
|
||||
),
|
||||
AuthModule,
|
||||
I18nModule,
|
||||
WorkspaceCacheStorageModule,
|
||||
WorkspaceMetadataCacheModule,
|
||||
|
||||
@@ -24,6 +24,18 @@ import { KeyValuePair } from 'src/engine/core-modules/key-value-pair/key-value-p
|
||||
import { PostgresCredentials } from 'src/engine/core-modules/postgres-credentials/postgres-credentials.entity';
|
||||
import { WorkspaceSSOIdentityProvider } from 'src/engine/core-modules/sso/workspace-sso-identity-provider.entity';
|
||||
import { UserWorkspace } from 'src/engine/core-modules/user-workspace/user-workspace.entity';
|
||||
import { ViewFieldDTO } from 'src/engine/core-modules/view/dtos/view-field.dto';
|
||||
import { ViewFilterGroupDTO } from 'src/engine/core-modules/view/dtos/view-filter-group.dto';
|
||||
import { ViewFilterDTO } from 'src/engine/core-modules/view/dtos/view-filter.dto';
|
||||
import { ViewGroupDTO } from 'src/engine/core-modules/view/dtos/view-group.dto';
|
||||
import { ViewSortDTO } from 'src/engine/core-modules/view/dtos/view-sort.dto';
|
||||
import { ViewDTO } from 'src/engine/core-modules/view/dtos/view.dto';
|
||||
import { ViewField } from 'src/engine/core-modules/view/entities/view-field.entity';
|
||||
import { ViewFilterGroup } from 'src/engine/core-modules/view/entities/view-filter-group.entity';
|
||||
import { ViewFilter } from 'src/engine/core-modules/view/entities/view-filter.entity';
|
||||
import { ViewGroup } from 'src/engine/core-modules/view/entities/view-group.entity';
|
||||
import { ViewSort } from 'src/engine/core-modules/view/entities/view-sort.entity';
|
||||
import { View } from 'src/engine/core-modules/view/entities/view.entity';
|
||||
import { Webhook } from 'src/engine/core-modules/webhook/webhook.entity';
|
||||
import { AgentHandoffEntity } from 'src/engine/metadata-modules/agent/agent-handoff.entity';
|
||||
import { AgentEntity } from 'src/engine/metadata-modules/agent/agent.entity';
|
||||
@@ -144,6 +156,33 @@ export class Workspace {
|
||||
@OneToMany(() => ApiKey, (apiKey) => apiKey.workspace)
|
||||
apiKeys: Relation<ApiKey[]>;
|
||||
|
||||
@Field(() => [ViewDTO], { nullable: true })
|
||||
@OneToMany(() => View, (view) => view.workspace)
|
||||
views: Relation<View[]>;
|
||||
|
||||
@Field(() => [ViewFieldDTO], { nullable: true })
|
||||
@OneToMany(() => ViewField, (viewField) => viewField.workspace)
|
||||
viewFields: Relation<ViewField[]>;
|
||||
|
||||
@Field(() => [ViewFilterDTO], { nullable: true })
|
||||
@OneToMany(() => ViewFilter, (viewFilter) => viewFilter.workspace)
|
||||
viewFilters: Relation<ViewFilter[]>;
|
||||
|
||||
@Field(() => [ViewFilterGroupDTO], { nullable: true })
|
||||
@OneToMany(
|
||||
() => ViewFilterGroup,
|
||||
(viewFilterGroup) => viewFilterGroup.workspace,
|
||||
)
|
||||
viewFilterGroups: Relation<ViewFilterGroup[]>;
|
||||
|
||||
@Field(() => [ViewGroupDTO], { nullable: true })
|
||||
@OneToMany(() => ViewGroup, (viewGroup) => viewGroup.workspace)
|
||||
viewGroups: Relation<ViewGroup[]>;
|
||||
|
||||
@Field(() => [ViewSortDTO], { nullable: true })
|
||||
@OneToMany(() => ViewSort, (viewSort) => viewSort.workspace)
|
||||
viewSorts: Relation<ViewSort[]>;
|
||||
|
||||
@Field()
|
||||
@Column({ default: 1 })
|
||||
metadataVersion: number;
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user