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:
Jérémy M
2024-10-24 15:38:52 +02:00
committed by GitHub
parent 68a060a046
commit e8d96cfd10
61 changed files with 1408 additions and 508 deletions

View File

@ -0,0 +1,17 @@
import { RecordGroupDefinition } from '@/object-record/record-group/types/RecordGroupDefinition';
import { ViewGroup } from '@/views/types/ViewGroup';
export const mapRecordGroupDefinitionsToViewGroups = (
groupDefinitions: RecordGroupDefinition[],
): ViewGroup[] => {
return groupDefinitions.map(
(groupDefinition): ViewGroup => ({
__typename: 'ViewGroup',
id: groupDefinition.id,
fieldMetadataId: groupDefinition.fieldMetadataId,
position: groupDefinition.position,
isVisible: groupDefinition.isVisible ?? true,
fieldValue: groupDefinition.value ?? '',
}),
);
};

View File

@ -0,0 +1,79 @@
import { isDefined } from '~/utils/isDefined';
import { ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
import {
RecordGroupDefinition,
RecordGroupDefinitionType,
} from '@/object-record/record-group/types/RecordGroupDefinition';
import { ViewGroup } from '@/views/types/ViewGroup';
import { FieldMetadataType } from '~/generated-metadata/graphql';
export const mapViewGroupsToRecordGroupDefinitions = ({
objectMetadataItem,
viewGroups,
}: {
objectMetadataItem: ObjectMetadataItem;
viewGroups: ViewGroup[];
}): RecordGroupDefinition[] => {
if (viewGroups?.length === 0) {
return [];
}
const fieldMetadataId = viewGroups?.[0]?.fieldMetadataId;
const selectFieldMetadataItem = objectMetadataItem.fields.find(
(field) =>
field.id === fieldMetadataId && field.type === FieldMetadataType.Select,
);
if (!selectFieldMetadataItem) {
return [];
}
if (!selectFieldMetadataItem.options) {
throw new Error(
`Select Field ${objectMetadataItem.nameSingular} has no options`,
);
}
const recordGroupDefinitionsFromViewGroups = viewGroups
.map((viewGroup) => {
const selectedOption = selectFieldMetadataItem.options?.find(
(option) => option.value === viewGroup.fieldValue,
);
if (!selectedOption) return null;
return {
id: viewGroup.id,
fieldMetadataId: viewGroup.fieldMetadataId,
type: RecordGroupDefinitionType.Value,
title: selectedOption.label,
value: selectedOption.value,
color: selectedOption.color,
position: viewGroup.position,
isVisible: viewGroup.isVisible,
} as RecordGroupDefinition;
})
.filter(isDefined)
.sort((a, b) => a.position - b.position);
if (selectFieldMetadataItem.isNullable === true) {
const noValueColumn = {
id: 'no-value',
title: 'No Value',
type: RecordGroupDefinitionType.NoValue,
value: null,
position:
recordGroupDefinitionsFromViewGroups
.map((option) => option.position)
.reduce((a, b) => Math.max(a, b), 0) + 1,
isVisible: true,
fieldMetadataId: selectFieldMetadataItem.id,
color: 'transparent',
} satisfies RecordGroupDefinition;
return [...recordGroupDefinitionsFromViewGroups, noValueColumn];
}
return recordGroupDefinitionsFromViewGroups;
};