Object view widget - Introduce new TABLE_WIDGET view type (#19545)

closes
https://discord.com/channels/1130383047699738754/1491549365263667230/1491804729397743666
This commit is contained in:
Etienne
2026-04-15 15:30:37 +02:00
committed by GitHub
parent 2fccd194f3
commit 94b8e34362
13 changed files with 111 additions and 28 deletions
@@ -735,6 +735,7 @@ enum ViewType {
KANBAN
CALENDAR
FIELDS_WIDGET
TABLE_WIDGET
}
enum ViewKey {
@@ -534,7 +534,7 @@ export interface View {
__typename: 'View'
}
export type ViewType = 'TABLE' | 'KANBAN' | 'CALENDAR' | 'FIELDS_WIDGET'
export type ViewType = 'TABLE' | 'KANBAN' | 'CALENDAR' | 'FIELDS_WIDGET' | 'TABLE_WIDGET'
export type ViewKey = 'INDEX'
@@ -9267,7 +9267,8 @@ export const enumViewType = {
TABLE: 'TABLE' as const,
KANBAN: 'KANBAN' as const,
CALENDAR: 'CALENDAR' as const,
FIELDS_WIDGET: 'FIELDS_WIDGET' as const
FIELDS_WIDGET: 'FIELDS_WIDGET' as const,
TABLE_WIDGET: 'TABLE_WIDGET' as const
}
export const enumViewKey = {
File diff suppressed because one or more lines are too long
@@ -13,6 +13,7 @@ import {
FindManyCommandMenuItemsDocument,
FindAllRecordPageLayoutsDocument,
FindFieldsWidgetViewsDocument,
FindTableWidgetViewsDocument,
FindManyFrontComponentsDocument,
FindManyLogicFunctionsDocument,
FindManyNavigationMenuItemsDocument,
@@ -44,6 +45,7 @@ const PAGE_LAYOUTS_GROUP_KEYS: MetadataEntityKey[] = [
const INDEX_VIEW_TYPES = [ViewType.TABLE, ViewType.KANBAN, ViewType.CALENDAR];
const FIELDS_WIDGET_VIEW_TYPES = [ViewType.FIELDS_WIDGET];
const TABLE_WIDGET_VIEW_TYPES = [ViewType.TABLE_WIDGET];
const hasOverlap = (
staleKeys: MetadataEntityKey[],
@@ -93,30 +95,42 @@ export const useLoadStaleMetadataEntities = () => {
variables: { viewTypes: FIELDS_WIDGET_VIEW_TYPES },
fetchPolicy: 'network-only',
}),
]).then(([indexViewsResult, fieldsWidgetViewsResult]) => {
const allViews = [
...(indexViewsResult.data?.getViews ?? []),
...(fieldsWidgetViewsResult.data?.getViews ?? []),
];
client.query({
query: FindTableWidgetViewsDocument,
variables: { viewTypes: TABLE_WIDGET_VIEW_TYPES },
fetchPolicy: 'network-only',
}),
]).then(
([
indexViewsResult,
fieldsWidgetViewsResult,
tableWidgetViewsResult,
]) => {
const allViews = [
...(indexViewsResult.data?.getViews ?? []),
...(fieldsWidgetViewsResult.data?.getViews ?? []),
...(tableWidgetViewsResult.data?.getViews ?? []),
];
const {
flatViews,
flatViewFields,
flatViewFilters,
flatViewSorts,
flatViewGroups,
flatViewFilterGroups,
flatViewFieldGroups,
} = splitViewWithRelated(allViews);
const {
flatViews,
flatViewFields,
flatViewFilters,
flatViewSorts,
flatViewGroups,
flatViewFilterGroups,
flatViewFieldGroups,
} = splitViewWithRelated(allViews);
replaceDraft('views', flatViews);
replaceDraft('viewFields', flatViewFields);
replaceDraft('viewFilters', flatViewFilters);
replaceDraft('viewSorts', flatViewSorts);
replaceDraft('viewGroups', flatViewGroups);
replaceDraft('viewFilterGroups', flatViewFilterGroups);
replaceDraft('viewFieldGroups', flatViewFieldGroups);
}),
replaceDraft('views', flatViews);
replaceDraft('viewFields', flatViewFields);
replaceDraft('viewFilters', flatViewFilters);
replaceDraft('viewSorts', flatViewSorts);
replaceDraft('viewGroups', flatViewGroups);
replaceDraft('viewFilterGroups', flatViewFilterGroups);
replaceDraft('viewFieldGroups', flatViewFieldGroups);
},
),
);
}
@@ -4,4 +4,5 @@ import { ViewVisibility } from '~/generated-metadata/graphql';
export const isViewDisplayableInNavigationMenu = (view: View): boolean =>
view.type !== ViewType.FIELDS_WIDGET &&
view.type !== ViewType.TABLE_WIDGET &&
view.visibility === ViewVisibility.WORKSPACE;
@@ -4,13 +4,13 @@ import { contextStoreCurrentViewIdComponentState } from '@/context-store/states/
import { useRecordIndexContextOrThrow } from '@/object-record/record-index/contexts/RecordIndexContext';
import { useLoadRecordIndexStates } from '@/object-record/record-index/hooks/useLoadRecordIndexStates';
import { recordIndexViewTypeState } from '@/object-record/record-index/states/recordIndexViewTypeState';
import { useSetAtomState } from '@/ui/utilities/state/jotai/hooks/useSetAtomState';
import { useUpdateCurrentView } from '@/views/hooks/useUpdateCurrentView';
import { viewsSelector } from '@/views/states/selectors/viewsSelector';
import { type GraphQLView } from '@/views/types/GraphQLView';
import { ViewType, viewTypeIconMapping } from '@/views/types/ViewType';
import { useGetAvailableFieldsForCalendar } from '@/views/view-picker/hooks/useGetAvailableFieldsForCalendar';
import { useGetAvailableFieldsToGroupRecordsBy } from '@/views/view-picker/hooks/useGetAvailableFieldsToGroupRecordsBy';
import { useSetAtomState } from '@/ui/utilities/state/jotai/hooks/useSetAtomState';
import { useStore } from 'jotai';
import { useCallback } from 'react';
import { assertUnreachable, isDefined } from 'twenty-shared/utils';
@@ -112,6 +112,7 @@ export const useSetViewTypeFromLayoutOptionsMenu = () => {
updateCurrentViewParams.mainGroupByFieldMetadataId = null;
return await updateCurrentView(updateCurrentViewParams);
}
case ViewType.TABLE_WIDGET:
case ViewType.FIELDS_WIDGET: {
return;
}
@@ -33,7 +33,7 @@ export const useCreateViewForRecordTableWidget = (pageLayoutId: string) => {
name: `${objectMetadataItem.labelPlural} Table`,
icon: objectMetadataItem.icon ?? 'IconTable',
objectMetadataId: objectMetadataItem.id,
type: ViewType.TABLE,
type: ViewType.TABLE_WIDGET,
},
},
objectMetadataItem.id,
@@ -0,0 +1,11 @@
import { VIEW_FRAGMENT } from '@/views/graphql/fragments/viewFragment';
import { gql } from '@apollo/client';
export const FIND_TABLE_WIDGET_VIEWS = gql`
${VIEW_FRAGMENT}
query FindTableWidgetViews($viewTypes: [ViewType!]) {
getViews(viewTypes: $viewTypes) {
...ViewFragment
}
}
`;
@@ -14,7 +14,8 @@ export const viewsFromObjectMetadataItemFamilySelector =
.filter(
(view) =>
view.objectMetadataId === objectMetadataItemId &&
view.type !== ViewType.FIELDS_WIDGET,
view.type !== ViewType.FIELDS_WIDGET &&
view.type !== ViewType.TABLE_WIDGET,
)
.sort((a, b) => a.position - b.position);
},
@@ -195,6 +195,13 @@ export const graphqlMocks = {
},
});
}),
metadataGraphql.query('FindTableWidgetViews', () => {
return HttpResponse.json({
data: {
getViews: mockedViews.filter((view) => view.type === 'TABLE_WIDGET'),
},
});
}),
metadataGraphql.query('FindAllRecordPageLayouts', () => {
return HttpResponse.json({
data: { getPageLayouts: [] },
@@ -0,0 +1,34 @@
import { type QueryRunner } from 'typeorm';
import { RegisteredInstanceCommand } from 'src/engine/core-modules/upgrade/decorators/registered-instance-command.decorator';
import { type FastInstanceCommand } from 'src/engine/core-modules/upgrade/interfaces/fast-instance-command.interface';
@RegisteredInstanceCommand('1.22.0', 1775752190522)
export class AddTableWidgetViewTypeFastInstanceCommand
implements FastInstanceCommand
{
public async up(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(
`ALTER TYPE "core"."view_type_enum" ADD VALUE IF NOT EXISTS 'TABLE_WIDGET' AFTER 'FIELDS_WIDGET'`,
);
}
public async down(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(
"CREATE TYPE \"core\".\"view_type_enum_old\" AS ENUM('TABLE', 'KANBAN', 'CALENDAR', 'FIELDS_WIDGET')",
);
await queryRunner.query(
'ALTER TABLE "core"."view" ALTER COLUMN "type" DROP DEFAULT',
);
await queryRunner.query(
'ALTER TABLE "core"."view" ALTER COLUMN "type" TYPE "core"."view_type_enum_old" USING "type"::"text"::"core"."view_type_enum_old"',
);
await queryRunner.query(
'ALTER TABLE "core"."view" ALTER COLUMN "type" SET DEFAULT \'TABLE\'',
);
await queryRunner.query('DROP TYPE "core"."view_type_enum"');
await queryRunner.query(
'ALTER TYPE "core"."view_type_enum_old" RENAME TO "view_type_enum"',
);
}
}
@@ -5,6 +5,7 @@ import { MigrateMessagingCalendarToCoreFastInstanceCommand } from 'src/database/
import { AddEmailThreadWidgetTypeFastInstanceCommand } from 'src/database/commands/upgrade-version-command/1-21/1-21-instance-command-fast-1775200000000-add-email-thread-widget-type';
import { AddStandalonePageFastInstanceCommand } from 'src/database/commands/upgrade-version-command/1-23/1-23-instance-command-fast-1775752781995-add-standalone-page';
import { AddPermissionFlagRoleIdIndexFastInstanceCommand } from 'src/database/commands/upgrade-version-command/1-22/1-22-instance-command-fast-1775749486425-add-permission-flag-role-id-index';
import { AddTableWidgetViewTypeFastInstanceCommand } from 'src/database/commands/upgrade-version-command/1-22/1-22-instance-command-fast-1775752190522-add-table-widget-view-type';
import { AddWorkspaceIdToIndirectEntitiesFastInstanceCommand } from 'src/database/commands/upgrade-version-command/1-22/1-22-instance-command-fast-1775758621017-add-workspace-id-to-indirect-entities';
import { AddWorkspaceIdIndexesAndFksFastInstanceCommand } from 'src/database/commands/upgrade-version-command/1-22/1-22-instance-command-fast-1775761294897-add-workspace-id-indexes-and-fks-to-indirect-entities';
import { DropObjectMetadataDataSourceFkFastInstanceCommand } from 'src/database/commands/upgrade-version-command/1-22/1-22-instance-command-fast-1775804361516-drop-object-metadata-data-source-fk';
@@ -17,6 +18,7 @@ export const INSTANCE_COMMANDS = [
AddViewFieldGroupIdIndexOnViewFieldFastInstanceCommand,
MigrateMessagingCalendarToCoreFastInstanceCommand,
AddEmailThreadWidgetTypeFastInstanceCommand,
AddTableWidgetViewTypeFastInstanceCommand,
AddStandalonePageFastInstanceCommand,
AddPermissionFlagRoleIdIndexFastInstanceCommand,
AddWorkspaceIdToIndirectEntitiesFastInstanceCommand,
@@ -3,4 +3,5 @@ export enum ViewType {
KANBAN = 'KANBAN',
CALENDAR = 'CALENDAR',
FIELDS_WIDGET = 'FIELDS_WIDGET',
TABLE_WIDGET = 'TABLE_WIDGET',
}