feat: view groups (#7176)
Fix #4244 and #4356 This pull request introduces the new "view groups" capability, enabling the reordering, hiding, and showing of columns in Kanban mode. The core enhancement includes the addition of a new entity named `ViewGroup`, which manages column behaviors and interactions. #### Key Changes: 1. **ViewGroup Entity**: The newly added `ViewGroup` entity is responsible for handling the organization and state of columns. This includes: - The ability to reorder columns. - The option to hide or show specific columns based on user preferences. #### Conclusion: This PR adds a significant new feature that enhances the flexibility of Kanban views through the `ViewGroup` entity. We'll later add the view group logic to table view too. --------- Co-authored-by: Lucas Bordeau <bordeau.lucas@gmail.com>
This commit is contained in:
@ -1,9 +1,12 @@
|
||||
import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular';
|
||||
import { useCreateOneRecord } from '@/object-record/hooks/useCreateOneRecord';
|
||||
import { RecordIndexRootPropsContext } from '@/object-record/record-index/contexts/RecordIndexRootPropsContext';
|
||||
import { getSnapshotValue } from '@/ui/utilities/recoil-scope/utils/getSnapshotValue';
|
||||
import { useRecoilComponentCallbackStateV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentCallbackStateV2';
|
||||
import { usePersistViewFieldRecords } from '@/views/hooks/internal/usePersistViewFieldRecords';
|
||||
import { useCreateViewFiltersAndSorts } from '@/views/hooks/useCreateViewFiltersAndSorts';
|
||||
import { usePersistViewFilterRecords } from '@/views/hooks/internal/usePersistViewFilterRecords';
|
||||
import { usePersistViewGroupRecords } from '@/views/hooks/internal/usePersistViewGroupRecords';
|
||||
import { usePersistViewSortRecords } from '@/views/hooks/internal/usePersistViewSortRecords';
|
||||
import { useGetViewFiltersCombined } from '@/views/hooks/useGetCombinedViewFilters';
|
||||
import { useGetViewSortsCombined } from '@/views/hooks/useGetCombinedViewSorts';
|
||||
import { useGetViewFromCache } from '@/views/hooks/useGetViewFromCache';
|
||||
@ -11,6 +14,10 @@ import { currentViewIdComponentState } from '@/views/states/currentViewIdCompone
|
||||
import { isPersistingViewFieldsComponentState } from '@/views/states/isPersistingViewFieldsComponentState';
|
||||
import { GraphQLView } from '@/views/types/GraphQLView';
|
||||
import { View } from '@/views/types/View';
|
||||
import { ViewGroup } from '@/views/types/ViewGroup';
|
||||
import { ViewType } from '@/views/types/ViewType';
|
||||
import { isNonEmptyArray } from '@sniptt/guards';
|
||||
import { useContext } from 'react';
|
||||
import { useRecoilCallback } from 'recoil';
|
||||
import { isDefined } from 'twenty-ui';
|
||||
import { v4 } from 'uuid';
|
||||
@ -35,12 +42,18 @@ export const useCreateViewFromCurrentView = (viewBarComponentId?: string) => {
|
||||
|
||||
const { createViewFieldRecords } = usePersistViewFieldRecords();
|
||||
|
||||
const { createViewFiltersAndSorts } = useCreateViewFiltersAndSorts();
|
||||
|
||||
const { getViewSortsCombined } = useGetViewSortsCombined(viewBarComponentId);
|
||||
const { getViewFiltersCombined } =
|
||||
useGetViewFiltersCombined(viewBarComponentId);
|
||||
|
||||
const { createViewSortRecords } = usePersistViewSortRecords();
|
||||
|
||||
const { createViewGroupRecords } = usePersistViewGroupRecords();
|
||||
|
||||
const { createViewFilterRecords } = usePersistViewFilterRecords();
|
||||
|
||||
const { objectMetadataItem } = useContext(RecordIndexRootPropsContext);
|
||||
|
||||
const createViewFromCurrentView = useRecoilCallback(
|
||||
({ snapshot, set }) =>
|
||||
async (
|
||||
@ -93,20 +106,56 @@ export const useCreateViewFromCurrentView = (viewBarComponentId?: string) => {
|
||||
|
||||
await createViewFieldRecords(view.viewFields, newView);
|
||||
|
||||
if (type === ViewType.Kanban) {
|
||||
if (!isNonEmptyArray(view.viewGroups)) {
|
||||
if (!isDefined(kanbanFieldMetadataId)) {
|
||||
throw new Error('Kanban view must have a kanban field');
|
||||
}
|
||||
|
||||
const viewGroupsToCreate =
|
||||
objectMetadataItem?.fields
|
||||
?.find((field) => field.id === kanbanFieldMetadataId)
|
||||
?.options?.map(
|
||||
(option, index) =>
|
||||
({
|
||||
id: v4(),
|
||||
__typename: 'ViewGroup',
|
||||
fieldMetadataId: kanbanFieldMetadataId,
|
||||
fieldValue: option.value,
|
||||
isVisible: true,
|
||||
position: index,
|
||||
}) satisfies ViewGroup,
|
||||
) ?? [];
|
||||
|
||||
viewGroupsToCreate.push({
|
||||
__typename: 'ViewGroup',
|
||||
id: v4(),
|
||||
fieldValue: '',
|
||||
position: viewGroupsToCreate.length,
|
||||
isVisible: true,
|
||||
fieldMetadataId: kanbanFieldMetadataId,
|
||||
} satisfies ViewGroup);
|
||||
|
||||
await createViewGroupRecords(viewGroupsToCreate, newView);
|
||||
} else {
|
||||
await createViewGroupRecords(view.viewGroups, newView);
|
||||
}
|
||||
}
|
||||
|
||||
if (shouldCopyFiltersAndSorts === true) {
|
||||
const sourceViewCombinedFilters = getViewFiltersCombined(view.id);
|
||||
const sourceViewCombinedSorts = getViewSortsCombined(view.id);
|
||||
|
||||
await createViewFiltersAndSorts(
|
||||
newView.id,
|
||||
sourceViewCombinedFilters,
|
||||
sourceViewCombinedSorts,
|
||||
);
|
||||
await createViewSortRecords(sourceViewCombinedSorts, view);
|
||||
await createViewFilterRecords(sourceViewCombinedFilters, view);
|
||||
}
|
||||
|
||||
set(isPersistingViewFieldsCallbackState, false);
|
||||
},
|
||||
[
|
||||
objectMetadataItem,
|
||||
createViewSortRecords,
|
||||
createViewFilterRecords,
|
||||
createOneRecord,
|
||||
createViewFieldRecords,
|
||||
getViewSortsCombined,
|
||||
@ -114,7 +163,7 @@ export const useCreateViewFromCurrentView = (viewBarComponentId?: string) => {
|
||||
currentViewIdCallbackState,
|
||||
getViewFromCache,
|
||||
isPersistingViewFieldsCallbackState,
|
||||
createViewFiltersAndSorts,
|
||||
createViewGroupRecords,
|
||||
],
|
||||
);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user