[Requires "warm" cache flush (no immediate downtime before flush)] Migrate viewGroup.fieldMetadataId -> view.mainGroupByFieldMetadataId (1/3) (#16206)

In this PR (1/3)
- introduce view.mainGroupByFieldMetadataId as the new reference
determining which fieldMetadataId is used in a grouped view, in order to
deprecate viewGroup.fieldMetadataId which creates inconsistencies.
view.mainGroupByFieldMetadataId is now filled at every view creation,
though not in use yet.
- Introduce a command to backfill view.mainGroupByFieldMetadataId for
existing views + delete all viewGroup.fieldMetadataId with a
fieldMetadataId that is not view.mainGroupByFieldMetadataId. (It should
concern 37 active workspaces)
- Temporarily disable the option to change a grouped view's
fieldMetadataId as for now it creates inconsistencies. This feature can
be reintroduced when we have done the full migration.

In a next PR
- (2/3) use view.mainGroupByFieldMetadataId instead of
viewGroup.fieldMetadataId. In FE we may keep viewGroup.fieldMetadataId
as a state (TBD). View groups will now be created / deleted as a side
effect of view's mainGroupByFieldMetadataId update.
- (3/3) remove viewGroup.fieldMetadataId

---------

Co-authored-by: Charles Bochet <charles@twenty.com>
This commit is contained in:
Marie
2025-12-02 16:31:07 +01:00
committed by GitHub
parent aa729a2a0a
commit 77409b6eb2
71 changed files with 484 additions and 99 deletions
@@ -113,13 +113,10 @@ export const ObjectOptionsDropdownRecordGroupsContent = () => {
>
{currentView?.key !== 'INDEX' && (
<>
<SelectableListItem
itemId="GroupBy"
onEnter={() => onContentChange('recordGroupFields')}
>
<SelectableListItem itemId="GroupBy">
<MenuItem
focused={selectedItemId === 'GroupBy'}
onClick={() => onContentChange('recordGroupFields')}
disabled
LeftIcon={IconLayoutList}
text={t`Group by`}
contextualText={recordGroupFieldMetadata?.label}
@@ -3,6 +3,7 @@ import { contextStoreCurrentViewIdComponentState } from '@/context-store/states/
import { type FieldMetadataItem } from '@/object-metadata/types/FieldMetadataItem';
import { useSetRecordGroups } from '@/object-record/record-group/hooks/useSetRecordGroups';
import { useRecoilComponentCallbackState } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentCallbackState';
import { usePersistView } from '@/views/hooks/internal/usePersistView';
import { usePersistViewGroupRecords } from '@/views/hooks/internal/usePersistViewGroup';
import { useGetViewFromPrefetchState } from '@/views/hooks/useGetViewFromPrefetchState';
import { type ViewGroup } from '@/views/types/ViewGroup';
@@ -23,6 +24,8 @@ export const useHandleRecordGroupField = () => {
const { setRecordGroupsFromViewGroups } = useSetRecordGroups();
const { updateView } = usePersistView();
const handleRecordGroupFieldChange = useRecoilCallback(
({ snapshot }) =>
async (fieldMetadataItem: FieldMetadataItem) => {
@@ -34,7 +37,7 @@ export const useHandleRecordGroupField = () => {
return;
}
const view = await getViewFromPrefetchState(currentViewId);
const view = getViewFromPrefetchState(currentViewId);
if (isUndefinedOrNull(view)) {
return;
@@ -47,6 +50,13 @@ export const useHandleRecordGroupField = () => {
return;
}
await updateView({
id: view.id,
input: {
mainGroupByFieldMetadataId: fieldMetadataItem.id,
},
});
const existingGroupKeys = new Set(
view.viewGroups.map(
(group) => `${group.fieldMetadataId}:${group.fieldValue}`,
@@ -124,10 +134,11 @@ export const useHandleRecordGroupField = () => {
}
},
[
objectMetadataItem,
currentViewIdCallbackState,
getViewFromPrefetchState,
updateView,
setRecordGroupsFromViewGroups,
objectMetadataItem,
createViewGroups,
deleteViewGroups,
],
@@ -24,6 +24,7 @@ export const VIEW_FRAGMENT = gql`
openRecordIn
kanbanAggregateOperation
kanbanAggregateOperationFieldMetadataId
# mainGroupByFieldMetadataId
anyFieldFilterValue
calendarFieldMetadataId
calendarLayout
@@ -77,7 +77,7 @@ export const useCreateViewFromCurrentView = (viewBarComponentId?: string) => {
id,
name,
icon,
kanbanFieldMetadataId,
mainGroupByFieldMetadataId,
calendarFieldMetadataId,
type,
visibility,
@@ -87,7 +87,7 @@ export const useCreateViewFromCurrentView = (viewBarComponentId?: string) => {
| 'id'
| 'name'
| 'icon'
| 'kanbanFieldMetadataId'
| 'mainGroupByFieldMetadataId'
| 'calendarFieldMetadataId'
| 'type'
| 'visibility'
@@ -133,6 +133,9 @@ export const useCreateViewFromCurrentView = (viewBarComponentId?: string) => {
shouldCopyFiltersAndSortsAndAggregate
? sourceView.kanbanAggregateOperationFieldMetadataId
: undefined,
mainGroupByFieldMetadataId: shouldCopyFiltersAndSortsAndAggregate
? sourceView.mainGroupByFieldMetadataId
: mainGroupByFieldMetadataId,
type: convertViewTypeToCore(viewType),
objectMetadataId: sourceView.objectMetadataId,
openRecordIn: convertViewOpenRecordInToCore(
@@ -179,19 +182,19 @@ export const useCreateViewFromCurrentView = (viewBarComponentId?: string) => {
}
if (type === ViewType.Kanban) {
if (!isDefined(kanbanFieldMetadataId)) {
if (!isDefined(mainGroupByFieldMetadataId)) {
throw new Error('Kanban view must have a kanban field');
}
const viewGroupsToCreate =
objectMetadataItem.fields
?.find((field) => field.id === kanbanFieldMetadataId)
?.find((field) => field.id === mainGroupByFieldMetadataId)
?.options?.map(
(option, index) =>
({
id: v4(),
__typename: 'ViewGroup',
fieldMetadataId: kanbanFieldMetadataId,
fieldMetadataId: mainGroupByFieldMetadataId,
fieldValue: option.value,
isVisible: true,
position: index,
@@ -204,7 +207,7 @@ export const useCreateViewFromCurrentView = (viewBarComponentId?: string) => {
fieldValue: '',
position: viewGroupsToCreate.length,
isVisible: true,
fieldMetadataId: kanbanFieldMetadataId,
fieldMetadataId: mainGroupByFieldMetadataId,
} satisfies ViewGroup);
const groupResult = await createViewGroups({
@@ -45,7 +45,9 @@ export const useSaveCurrentViewGroups = () => {
const existingField = currentViewGroups.find(
(currentViewGroup) =>
currentViewGroup.fieldValue === viewGroupToSave.fieldValue,
currentViewGroup.fieldValue === viewGroupToSave.fieldValue &&
currentViewGroup.fieldMetadataId ===
viewGroupToSave.fieldMetadataId,
);
if (isUndefinedOrNull(existingField)) {
@@ -116,7 +118,9 @@ export const useSaveCurrentViewGroups = () => {
.map((viewGroupToSave) => {
const existingField = currentViewGroups.find(
(currentViewGroup) =>
currentViewGroup.fieldValue === viewGroupToSave.fieldValue,
currentViewGroup.fieldValue === viewGroupToSave.fieldValue &&
currentViewGroup.fieldMetadataId ===
viewGroupToSave.fieldMetadataId,
);
if (isUndefinedOrNull(existingField)) {
@@ -156,7 +160,9 @@ export const useSaveCurrentViewGroups = () => {
(viewFieldToSave) =>
!currentViewGroups.some(
(currentViewGroup) =>
currentViewGroup.fieldValue === viewFieldToSave.fieldValue,
currentViewGroup.fieldValue === viewFieldToSave.fieldValue &&
currentViewGroup.fieldMetadataId ===
viewFieldToSave.fieldMetadataId,
),
);
@@ -30,6 +30,7 @@ export type CoreViewWithRelations = {
'workspaceId' | 'createdAt' | 'updatedAt'
>[];
viewSorts: CoreViewSortEssential[];
mainGroupByFieldMetadataId?: string | null;
kanbanAggregateOperation?: AggregateOperations | null;
kanbanAggregateOperationFieldMetadataId?: string | null;
calendarFieldMetadataId?: string | null;
@@ -15,10 +15,7 @@ export type GraphQLView = {
name: string;
type: ViewType;
key: ViewKey | null;
/**
* @deprecated Use `viewGroups.fieldMetadataId` instead.
*/
kanbanFieldMetadataId: string;
mainGroupByFieldMetadataId?: string | null;
kanbanAggregateOperation?: AggregateOperations | null;
kanbanAggregateOperationFieldMetadataId?: string | null;
objectMetadataId: string;
@@ -22,12 +22,9 @@ export type View = {
viewFilters: ViewFilter[];
viewFilterGroups?: ViewFilterGroup[];
viewSorts: CoreViewSortEssential[];
/**
* @deprecated Use `viewGroups.fieldMetadataId` instead.
*/
kanbanFieldMetadataId: string;
kanbanAggregateOperation: AggregateOperations | null;
kanbanAggregateOperationFieldMetadataId: string | null;
mainGroupByFieldMetadataId?: string | null;
calendarFieldMetadataId?: string | null;
calendarLayout?: ViewCalendarLayout | null;
position: number;
@@ -37,7 +37,7 @@ describe('mapRecordFilterGroupToViewFilterGroup', () => {
viewFields: [],
viewGroups: [],
viewSorts: [],
kanbanFieldMetadataId: '',
mainGroupByFieldMetadataId: '',
kanbanAggregateOperation: AggregateOperations.COUNT,
icon: '',
kanbanAggregateOperationFieldMetadataId: '',
@@ -38,7 +38,7 @@ export const convertCoreViewToView = (
convertCoreViewFilterGroupToViewFilterGroup,
),
viewSorts: coreView.viewSorts,
kanbanFieldMetadataId: '',
mainGroupByFieldMetadataId: coreView.mainGroupByFieldMetadataId ?? null,
kanbanAggregateOperation: coreView.kanbanAggregateOperation ?? null,
kanbanAggregateOperationFieldMetadataId:
coreView.kanbanAggregateOperationFieldMetadataId ?? null,
@@ -16,7 +16,7 @@ export const getObjectMetadataItemViews = (
key: view.key,
position: view.position,
objectMetadataId: view.objectMetadataId,
kanbanFieldMetadataId: view.kanbanFieldMetadataId,
mainGroupByFieldMetadataId: view.mainGroupByFieldMetadataId,
kanbanAggregateOperation: view.kanbanAggregateOperation,
kanbanAggregateOperationFieldMetadataId:
view.kanbanAggregateOperationFieldMetadataId,
@@ -4,8 +4,8 @@ import {
RecordGroupDefinitionType,
} from '@/object-record/record-group/types/RecordGroupDefinition';
import { type ViewGroup } from '@/views/types/ViewGroup';
import { FieldMetadataType } from '~/generated-metadata/graphql';
import { isDefined } from 'twenty-shared/utils';
import { FieldMetadataType } from '~/generated-metadata/graphql';
export const mapViewGroupsToRecordGroupDefinitions = ({
objectMetadataItem,
@@ -36,6 +36,10 @@ export const mapViewGroupsToRecordGroupDefinitions = ({
const recordGroupDefinitionsFromViewGroups = viewGroups
.map((viewGroup) => {
if (viewGroup.fieldMetadataId !== selectFieldMetadataItem.id) {
return null;
}
const selectedOption = selectFieldMetadataItem.options?.find(
(option) => option.value === viewGroup.fieldValue,
);
@@ -33,7 +33,7 @@ import { viewPickerCalendarFieldMetadataIdComponentState } from '@/views/view-pi
import { viewPickerInputNameComponentState } from '@/views/view-picker/states/viewPickerInputNameComponentState';
import { viewPickerIsDirtyComponentState } from '@/views/view-picker/states/viewPickerIsDirtyComponentState';
import { viewPickerIsPersistingComponentState } from '@/views/view-picker/states/viewPickerIsPersistingComponentState';
import { viewPickerKanbanFieldMetadataIdComponentState } from '@/views/view-picker/states/viewPickerKanbanFieldMetadataIdComponentState';
import { viewPickerMainGroupByFieldMetadataIdComponentState } from '@/views/view-picker/states/viewPickerMainGroupByFieldMetadataIdComponentState';
import { viewPickerSelectedIconComponentState } from '@/views/view-picker/states/viewPickerSelectedIconComponentState';
import { viewPickerTypeComponentState } from '@/views/view-picker/states/viewPickerTypeComponentState';
import { useLingui } from '@lingui/react/macro';
@@ -74,8 +74,12 @@ export const ViewPickerContentCreateMode = () => {
viewPickerIsDirtyComponentState,
);
const [viewPickerKanbanFieldMetadataId, setViewPickerKanbanFieldMetadataId] =
useRecoilComponentState(viewPickerKanbanFieldMetadataIdComponentState);
const [
viewPickerMainGroupByFieldMetadataId,
setViewPickerMainGroupByFieldMetadataId,
] = useRecoilComponentState(
viewPickerMainGroupByFieldMetadataIdComponentState,
);
const [
viewPickerCalendarFieldMetadataId,
@@ -188,10 +192,10 @@ export const ViewPickerContentCreateMode = () => {
<Select
label={t`Stages`}
fullWidth
value={viewPickerKanbanFieldMetadataId}
value={viewPickerMainGroupByFieldMetadataId}
onChange={(value) => {
setViewPickerIsDirty(true);
setViewPickerKanbanFieldMetadataId(value);
setViewPickerMainGroupByFieldMetadataId(value);
}}
options={
availableFieldsForKanban.length > 0
@@ -14,7 +14,7 @@ import { viewPickerCalendarFieldMetadataIdComponentState } from '@/views/view-pi
import { viewPickerInputNameComponentState } from '@/views/view-picker/states/viewPickerInputNameComponentState';
import { viewPickerIsDirtyComponentState } from '@/views/view-picker/states/viewPickerIsDirtyComponentState';
import { viewPickerIsPersistingComponentState } from '@/views/view-picker/states/viewPickerIsPersistingComponentState';
import { viewPickerKanbanFieldMetadataIdComponentState } from '@/views/view-picker/states/viewPickerKanbanFieldMetadataIdComponentState';
import { viewPickerMainGroupByFieldMetadataIdComponentState } from '@/views/view-picker/states/viewPickerMainGroupByFieldMetadataIdComponentState';
import { viewPickerReferenceViewIdComponentState } from '@/views/view-picker/states/viewPickerReferenceViewIdComponentState';
import { viewPickerSelectedIconComponentState } from '@/views/view-picker/states/viewPickerSelectedIconComponentState';
import { viewPickerTypeComponentState } from '@/views/view-picker/states/viewPickerTypeComponentState';
@@ -36,8 +36,12 @@ export const ViewPickerContentEffect = () => {
);
const { viewPickerMode } = useViewPickerMode();
const [viewPickerKanbanFieldMetadataId, setViewPickerKanbanFieldMetadataId] =
useRecoilComponentState(viewPickerKanbanFieldMetadataIdComponentState);
const [
viewPickerMainGroupByFieldMetadataId,
setViewPickerMainGroupByFieldMetadataId,
] = useRecoilComponentState(
viewPickerMainGroupByFieldMetadataIdComponentState,
);
const [
viewPickerCalendarFieldMetadataId,
@@ -112,12 +116,12 @@ export const ViewPickerContentEffect = () => {
if (
isDefined(referenceView) &&
availableFieldsForKanban.length > 0 &&
viewPickerKanbanFieldMetadataId === ''
viewPickerMainGroupByFieldMetadataId === ''
) {
setViewPickerKanbanFieldMetadataId(
// TODO: replace with viewGroups.fieldMetadataId
referenceView.kanbanFieldMetadataId !== ''
? referenceView.kanbanFieldMetadataId
setViewPickerMainGroupByFieldMetadataId(
isDefined(referenceView.mainGroupByFieldMetadataId) &&
referenceView.mainGroupByFieldMetadataId !== ''
? referenceView.mainGroupByFieldMetadataId
: availableFieldsForKanban[0].id,
);
}
@@ -136,8 +140,8 @@ export const ViewPickerContentEffect = () => {
}, [
referenceView,
availableFieldsForKanban,
viewPickerKanbanFieldMetadataId,
setViewPickerKanbanFieldMetadataId,
viewPickerMainGroupByFieldMetadataId,
setViewPickerMainGroupByFieldMetadataId,
availableFieldsForCalendar,
viewPickerCalendarFieldMetadataId,
setViewPickerCalendarFieldMetadataId,
@@ -7,7 +7,7 @@ import { useGetAvailableFieldsForKanban } from '@/views/view-picker/hooks/useGet
import { useViewPickerMode } from '@/views/view-picker/hooks/useViewPickerMode';
import { viewPickerCalendarFieldMetadataIdComponentState } from '@/views/view-picker/states/viewPickerCalendarFieldMetadataIdComponentState';
import { viewPickerIsPersistingComponentState } from '@/views/view-picker/states/viewPickerIsPersistingComponentState';
import { viewPickerKanbanFieldMetadataIdComponentState } from '@/views/view-picker/states/viewPickerKanbanFieldMetadataIdComponentState';
import { viewPickerMainGroupByFieldMetadataIdComponentState } from '@/views/view-picker/states/viewPickerMainGroupByFieldMetadataIdComponentState';
import { viewPickerTypeComponentState } from '@/views/view-picker/states/viewPickerTypeComponentState';
import { useLingui } from '@lingui/react/macro';
import { Button } from 'twenty-ui/input';
@@ -24,8 +24,8 @@ export const ViewPickerCreateButton = () => {
const viewPickerIsPersisting = useRecoilComponentValue(
viewPickerIsPersistingComponentState,
);
const viewPickerKanbanFieldMetadataId = useRecoilComponentValue(
viewPickerKanbanFieldMetadataIdComponentState,
const viewPickerMainGroupByFieldMetadataId = useRecoilComponentValue(
viewPickerMainGroupByFieldMetadataIdComponentState,
);
const viewPickerCalendarFieldMetadataId = useRecoilComponentValue(
viewPickerCalendarFieldMetadataIdComponentState,
@@ -88,7 +88,7 @@ export const ViewPickerCreateButton = () => {
if (
viewPickerType !== ViewType.Kanban ||
viewPickerKanbanFieldMetadataId !== ''
viewPickerMainGroupByFieldMetadataId !== ''
) {
return (
<Button
@@ -101,7 +101,7 @@ export const ViewPickerCreateButton = () => {
disabled={
viewPickerIsPersisting ||
(viewPickerType === ViewType.Kanban &&
viewPickerKanbanFieldMetadataId === '') ||
viewPickerMainGroupByFieldMetadataId === '') ||
(viewPickerType === ViewType.Calendar &&
viewPickerCalendarFieldMetadataId === '')
}
@@ -5,7 +5,7 @@ import { useDeleteViewFromCurrentState } from '@/views/view-picker/hooks/useDele
import { useGetAvailableFieldsForKanban } from '@/views/view-picker/hooks/useGetAvailableFieldsForKanban';
import { useViewPickerMode } from '@/views/view-picker/hooks/useViewPickerMode';
import { viewPickerIsPersistingComponentState } from '@/views/view-picker/states/viewPickerIsPersistingComponentState';
import { viewPickerKanbanFieldMetadataIdComponentState } from '@/views/view-picker/states/viewPickerKanbanFieldMetadataIdComponentState';
import { viewPickerMainGroupByFieldMetadataIdComponentState } from '@/views/view-picker/states/viewPickerMainGroupByFieldMetadataIdComponentState';
import { viewPickerTypeComponentState } from '@/views/view-picker/states/viewPickerTypeComponentState';
import { t } from '@lingui/core/macro';
import { Button } from 'twenty-ui/input';
@@ -19,8 +19,8 @@ export const ViewPickerEditButton = () => {
const viewPickerIsPersisting = useRecoilComponentValue(
viewPickerIsPersistingComponentState,
);
const viewPickerKanbanFieldMetadataId = useRecoilComponentValue(
viewPickerKanbanFieldMetadataIdComponentState,
const viewPickerMainGroupByFieldMetadataId = useRecoilComponentValue(
viewPickerMainGroupByFieldMetadataIdComponentState,
);
const { createViewFromCurrentState } = useCreateViewFromCurrentState();
@@ -60,7 +60,7 @@ export const ViewPickerEditButton = () => {
if (
viewPickerType === ViewType.Table ||
viewPickerKanbanFieldMetadataId !== ''
viewPickerMainGroupByFieldMetadataId !== ''
) {
return (
<Button
@@ -73,7 +73,7 @@ export const ViewPickerEditButton = () => {
disabled={
viewPickerIsPersisting ||
(viewPickerType === ViewType.Kanban &&
viewPickerKanbanFieldMetadataId === '')
viewPickerMainGroupByFieldMetadataId === '')
}
/>
);
@@ -2,12 +2,13 @@ import { useRecoilComponentCallbackState } from '@/ui/utilities/state/component-
import { getSnapshotValue } from '@/ui/utilities/state/utils/getSnapshotValue';
import { useChangeView } from '@/views/hooks/useChangeView';
import { useCreateViewFromCurrentView } from '@/views/hooks/useCreateViewFromCurrentView';
import { ViewType } from '@/views/types/ViewType';
import { useCloseAndResetViewPicker } from '@/views/view-picker/hooks/useCloseAndResetViewPicker';
import { viewPickerCalendarFieldMetadataIdComponentState } from '@/views/view-picker/states/viewPickerCalendarFieldMetadataIdComponentState';
import { viewPickerInputNameComponentState } from '@/views/view-picker/states/viewPickerInputNameComponentState';
import { viewPickerIsDirtyComponentState } from '@/views/view-picker/states/viewPickerIsDirtyComponentState';
import { viewPickerIsPersistingComponentState } from '@/views/view-picker/states/viewPickerIsPersistingComponentState';
import { viewPickerKanbanFieldMetadataIdComponentState } from '@/views/view-picker/states/viewPickerKanbanFieldMetadataIdComponentState';
import { viewPickerMainGroupByFieldMetadataIdComponentState } from '@/views/view-picker/states/viewPickerMainGroupByFieldMetadataIdComponentState';
import { viewPickerModeComponentState } from '@/views/view-picker/states/viewPickerModeComponentState';
import { viewPickerSelectedIconComponentState } from '@/views/view-picker/states/viewPickerSelectedIconComponentState';
import { viewPickerTypeComponentState } from '@/views/view-picker/states/viewPickerTypeComponentState';
@@ -30,9 +31,9 @@ export const useCreateViewFromCurrentState = () => {
viewPickerTypeComponentState,
);
const viewPickerKanbanFieldMetadataIdCallbackState =
const viewPickerMainGroupByFieldMetadataIdCallbackState =
useRecoilComponentCallbackState(
viewPickerKanbanFieldMetadataIdComponentState,
viewPickerMainGroupByFieldMetadataIdComponentState,
);
const viewPickerCalendarFieldMetadataIdCallbackState =
@@ -71,14 +72,15 @@ export const useCreateViewFromCurrentState = () => {
viewPickerSelectedIconCallbackState,
);
const type = getSnapshotValue(snapshot, viewPickerTypeCallbackState);
const kanbanFieldMetadataId = getSnapshotValue(
const mainGroupByFieldMetadataId = getSnapshotValue(
snapshot,
viewPickerKanbanFieldMetadataIdCallbackState,
viewPickerMainGroupByFieldMetadataIdCallbackState,
);
const calendarFieldMetadataId = getSnapshotValue(
snapshot,
viewPickerCalendarFieldMetadataIdCallbackState,
);
const viewPickerMode = getSnapshotValue(
snapshot,
viewPickerModeCallbackState,
@@ -99,7 +101,8 @@ export const useCreateViewFromCurrentState = () => {
name,
icon: iconKey,
type,
kanbanFieldMetadataId,
mainGroupByFieldMetadataId:
type === ViewType.Kanban ? mainGroupByFieldMetadataId : null,
calendarFieldMetadataId,
visibility,
},
@@ -118,7 +121,7 @@ export const useCreateViewFromCurrentState = () => {
viewPickerInputNameCallbackState,
viewPickerIsDirtyCallbackState,
viewPickerIsPersistingCallbackState,
viewPickerKanbanFieldMetadataIdCallbackState,
viewPickerMainGroupByFieldMetadataIdCallbackState,
viewPickerCalendarFieldMetadataIdCallbackState,
viewPickerSelectedIconCallbackState,
viewPickerTypeCallbackState,
@@ -1,9 +1,9 @@
import { createComponentState } from '@/ui/utilities/state/component-state/utils/createComponentState';
import { ViewComponentInstanceContext } from '@/views/states/contexts/ViewComponentInstanceContext';
export const viewPickerKanbanFieldMetadataIdComponentState =
export const viewPickerMainGroupByFieldMetadataIdComponentState =
createComponentState<string>({
key: 'viewPickerKanbanFieldMetadataIdComponentState',
key: 'viewPickerMainGroupByFieldMetadataIdComponentState',
defaultValue: '',
componentInstanceContext: ViewComponentInstanceContext,
});