feat: Add filter button to objects table and enable system objects access (#15856)
## Summary This PR replaces the Active/Inactive accordion sections with a modern filter dropdown button and enables full access to system objects in advanced mode. ## Changes ### UI Improvements - ✅ Replaced accordion sections with a filter button dropdown (matching the design pattern from the Group filter) - ✅ Added 'Deactivated' toggle filter (hidden by default, uses IconArchive) - ✅ Added 'System objects' toggle filter (only visible in advanced mode, uses IconSettings) - ✅ Fixed search input width to properly fill available space - ✅ Proper button sizing and alignment ### System Objects Support - ✅ Made system objects visible when 'System objects' filter is toggled on - ✅ System objects are now fully clickable and accessible - ✅ Updated object detail page to support system objects - ✅ Updated field creation/edit pages to support system objects - ✅ System objects can now have custom fields added ### Architecture - ✅ Implemented scalable filter architecture using a single filtered list - ✅ Easy to add more filters in the future (e.g., show remote objects) - ✅ All filters work independently and can be combined ## Testing - [x] Tested deactivated objects toggle - [x] Tested system objects toggle (only shows in advanced mode) - [x] Tested clicking on system objects - [x] Tested adding custom fields to system objects - [x] No linter errors ## Screenshots See attached screenshots in the conversation for the new filter UI. <!-- CURSOR_SUMMARY --> --- > [!NOTE] > Adds a filter dropdown to the Settings Objects table (incl. deactivated/system toggles), enables system objects across settings/field flows, seeds core views (workspace members, messages, threads, calendar events), and adds actions for Workspace Members. > > - **Settings UI** > - **Objects Table**: Replaces Active/Inactive sections with a single filterable list and dropdown (`Deactivated`, `System objects` in advanced mode); updates props to `objectMetadataItems` and removes accordion sections. > - **Search/UX**: Search input fills available space; inactive rows show activation/delete menu; active rows remain navigable. > - **Pages Updated**: `SettingsObjects`, `SettingsApplicationDetailContentTab` switch to new table API; object detail and new-field flows use `findObjectMetadataItemByNamePlural` (works with system objects). > - **Action Menu** > - **Workspace Members**: Adds `WORKSPACE_MEMBERS_ACTIONS_CONFIG` with "Manage members in settings" action; wired into `getActionConfig` for `WorkspaceMember`. > - **Server (Core Views Seed)** > - Adds default views: `workspaceMembersAllView`, `messagesAllView`, `messageThreadsAllView`, `calendarEventsAllView`; included in `prefillCoreViews`. > - Marks workflow entities as system (`WorkflowRun`, `WorkflowVersion`). > > <sup>Written by [Cursor Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit 6a2856df85104665bd986cc63f2d54f19bf668d0. This will update automatically on new commits. Configure [here](https://cursor.com/dashboard?tab=bugbot).</sup> <!-- /CURSOR_SUMMARY -->
This commit is contained in:
+8
@@ -16,8 +16,11 @@ import { type WorkspaceEntityManager } from 'src/engine/twenty-orm/entity-manage
|
||||
import { shouldSeedWorkspaceFavorite } from 'src/engine/utils/should-seed-workspace-favorite';
|
||||
import { prefillWorkspaceFavorites } from 'src/engine/workspace-manager/standard-objects-prefill-data/prefill-workspace-favorites';
|
||||
import { type ViewDefinition } from 'src/engine/workspace-manager/standard-objects-prefill-data/types/view-definition.interface';
|
||||
import { calendarEventsAllView } from 'src/engine/workspace-manager/standard-objects-prefill-data/views/calendar-events-all.view';
|
||||
import { companiesAllView } from 'src/engine/workspace-manager/standard-objects-prefill-data/views/companies-all.view';
|
||||
import { dashboardsAllView } from 'src/engine/workspace-manager/standard-objects-prefill-data/views/dashboards-all.view';
|
||||
import { messageThreadsAllView } from 'src/engine/workspace-manager/standard-objects-prefill-data/views/message-threads-all.view';
|
||||
import { messagesAllView } from 'src/engine/workspace-manager/standard-objects-prefill-data/views/messages-all.view';
|
||||
import { notesAllView } from 'src/engine/workspace-manager/standard-objects-prefill-data/views/notes-all.view';
|
||||
import { opportunitiesAllView } from 'src/engine/workspace-manager/standard-objects-prefill-data/views/opportunities-all.view';
|
||||
import { opportunitiesByStageView } from 'src/engine/workspace-manager/standard-objects-prefill-data/views/opportunity-by-stage.view';
|
||||
@@ -28,6 +31,7 @@ import { tasksByStatusView } from 'src/engine/workspace-manager/standard-objects
|
||||
import { workflowRunsAllView } from 'src/engine/workspace-manager/standard-objects-prefill-data/views/workflow-runs-all.view';
|
||||
import { workflowVersionsAllView } from 'src/engine/workspace-manager/standard-objects-prefill-data/views/workflow-versions-all.view';
|
||||
import { workflowsAllView } from 'src/engine/workspace-manager/standard-objects-prefill-data/views/workflows-all.view';
|
||||
import { workspaceMembersAllView } from 'src/engine/workspace-manager/standard-objects-prefill-data/views/workspace-members-all.view';
|
||||
|
||||
type PrefillCoreViewsArgs = {
|
||||
coreDataSource: DataSource;
|
||||
@@ -56,6 +60,10 @@ export const prefillCoreViews = async ({
|
||||
workflowVersionsAllView(objectMetadataItems, true),
|
||||
workflowRunsAllView(objectMetadataItems, true),
|
||||
dashboardsAllView(objectMetadataItems, true),
|
||||
workspaceMembersAllView(objectMetadataItems, true),
|
||||
messagesAllView(objectMetadataItems, true),
|
||||
messageThreadsAllView(objectMetadataItems, true),
|
||||
calendarEventsAllView(objectMetadataItems, true),
|
||||
];
|
||||
|
||||
const queryRunner = coreDataSource.createQueryRunner();
|
||||
|
||||
+122
@@ -0,0 +1,122 @@
|
||||
import { msg } from '@lingui/core/macro';
|
||||
|
||||
import { type ObjectMetadataEntity } from 'src/engine/metadata-modules/object-metadata/object-metadata.entity';
|
||||
import { DEFAULT_VIEW_FIELD_SIZE } from 'src/engine/workspace-manager/standard-objects-prefill-data/views/constants/DEFAULT_VIEW_FIELD_SIZE';
|
||||
import {
|
||||
BASE_OBJECT_STANDARD_FIELD_IDS,
|
||||
CALENDAR_EVENT_STANDARD_FIELD_IDS,
|
||||
} from 'src/engine/workspace-manager/workspace-sync-metadata/constants/standard-field-ids';
|
||||
import { STANDARD_OBJECT_IDS } from 'src/engine/workspace-manager/workspace-sync-metadata/constants/standard-object-ids';
|
||||
|
||||
export const calendarEventsAllView = (
|
||||
objectMetadataItems: ObjectMetadataEntity[],
|
||||
useCoreNaming = false,
|
||||
) => {
|
||||
const calendarEventObjectMetadata = objectMetadataItems.find(
|
||||
(object) => object.standardId === STANDARD_OBJECT_IDS.calendarEvent,
|
||||
);
|
||||
|
||||
if (!calendarEventObjectMetadata) {
|
||||
throw new Error('CalendarEvent object metadata not found');
|
||||
}
|
||||
|
||||
return {
|
||||
name: useCoreNaming ? msg`All {objectLabelPlural}` : 'All Calendar Events',
|
||||
objectMetadataId: calendarEventObjectMetadata.id ?? '',
|
||||
type: 'calendar',
|
||||
key: 'INDEX',
|
||||
position: 0,
|
||||
icon: 'IconCalendar',
|
||||
kanbanFieldMetadataId: '',
|
||||
calendarFieldMetadataId:
|
||||
calendarEventObjectMetadata.fields.find(
|
||||
(field) =>
|
||||
field.standardId === CALENDAR_EVENT_STANDARD_FIELD_IDS.startsAt,
|
||||
)?.id ?? '',
|
||||
calendarLayout: 'MONTH',
|
||||
filters: [],
|
||||
fields: [
|
||||
{
|
||||
fieldMetadataId:
|
||||
calendarEventObjectMetadata.fields.find(
|
||||
(field) =>
|
||||
field.standardId === CALENDAR_EVENT_STANDARD_FIELD_IDS.title,
|
||||
)?.id ?? '',
|
||||
position: 0,
|
||||
isVisible: true,
|
||||
size: DEFAULT_VIEW_FIELD_SIZE,
|
||||
},
|
||||
{
|
||||
fieldMetadataId:
|
||||
calendarEventObjectMetadata.fields.find(
|
||||
(field) =>
|
||||
field.standardId === CALENDAR_EVENT_STANDARD_FIELD_IDS.startsAt,
|
||||
)?.id ?? '',
|
||||
position: 1,
|
||||
isVisible: true,
|
||||
size: 150,
|
||||
},
|
||||
{
|
||||
fieldMetadataId:
|
||||
calendarEventObjectMetadata.fields.find(
|
||||
(field) =>
|
||||
field.standardId === CALENDAR_EVENT_STANDARD_FIELD_IDS.endsAt,
|
||||
)?.id ?? '',
|
||||
position: 2,
|
||||
isVisible: true,
|
||||
size: 150,
|
||||
},
|
||||
{
|
||||
fieldMetadataId:
|
||||
calendarEventObjectMetadata.fields.find(
|
||||
(field) =>
|
||||
field.standardId === CALENDAR_EVENT_STANDARD_FIELD_IDS.isFullDay,
|
||||
)?.id ?? '',
|
||||
position: 3,
|
||||
isVisible: true,
|
||||
size: 100,
|
||||
},
|
||||
{
|
||||
fieldMetadataId:
|
||||
calendarEventObjectMetadata.fields.find(
|
||||
(field) =>
|
||||
field.standardId === CALENDAR_EVENT_STANDARD_FIELD_IDS.location,
|
||||
)?.id ?? '',
|
||||
position: 4,
|
||||
isVisible: true,
|
||||
size: 150,
|
||||
},
|
||||
{
|
||||
fieldMetadataId:
|
||||
calendarEventObjectMetadata.fields.find(
|
||||
(field) =>
|
||||
field.standardId ===
|
||||
CALENDAR_EVENT_STANDARD_FIELD_IDS.conferenceLink,
|
||||
)?.id ?? '',
|
||||
position: 5,
|
||||
isVisible: true,
|
||||
size: 150,
|
||||
},
|
||||
{
|
||||
fieldMetadataId:
|
||||
calendarEventObjectMetadata.fields.find(
|
||||
(field) =>
|
||||
field.standardId === CALENDAR_EVENT_STANDARD_FIELD_IDS.isCanceled,
|
||||
)?.id ?? '',
|
||||
position: 6,
|
||||
isVisible: true,
|
||||
size: 100,
|
||||
},
|
||||
{
|
||||
fieldMetadataId:
|
||||
calendarEventObjectMetadata.fields.find(
|
||||
(field) =>
|
||||
field.standardId === BASE_OBJECT_STANDARD_FIELD_IDS.createdAt,
|
||||
)?.id ?? '',
|
||||
position: 7,
|
||||
isVisible: true,
|
||||
size: 150,
|
||||
},
|
||||
],
|
||||
};
|
||||
};
|
||||
+55
@@ -0,0 +1,55 @@
|
||||
import { msg } from '@lingui/core/macro';
|
||||
|
||||
import { type ObjectMetadataEntity } from 'src/engine/metadata-modules/object-metadata/object-metadata.entity';
|
||||
import { DEFAULT_VIEW_FIELD_SIZE } from 'src/engine/workspace-manager/standard-objects-prefill-data/views/constants/DEFAULT_VIEW_FIELD_SIZE';
|
||||
import {
|
||||
BASE_OBJECT_STANDARD_FIELD_IDS,
|
||||
MESSAGE_THREAD_STANDARD_FIELD_IDS,
|
||||
} from 'src/engine/workspace-manager/workspace-sync-metadata/constants/standard-field-ids';
|
||||
import { STANDARD_OBJECT_IDS } from 'src/engine/workspace-manager/workspace-sync-metadata/constants/standard-object-ids';
|
||||
|
||||
export const messageThreadsAllView = (
|
||||
objectMetadataItems: ObjectMetadataEntity[],
|
||||
useCoreNaming = false,
|
||||
) => {
|
||||
const messageThreadObjectMetadata = objectMetadataItems.find(
|
||||
(object) => object.standardId === STANDARD_OBJECT_IDS.messageThread,
|
||||
);
|
||||
|
||||
if (!messageThreadObjectMetadata) {
|
||||
throw new Error('MessageThread object metadata not found');
|
||||
}
|
||||
|
||||
return {
|
||||
name: useCoreNaming ? msg`All {objectLabelPlural}` : 'All Message Threads',
|
||||
objectMetadataId: messageThreadObjectMetadata.id ?? '',
|
||||
type: 'table',
|
||||
key: 'INDEX',
|
||||
position: 0,
|
||||
icon: 'IconList',
|
||||
kanbanFieldMetadataId: '',
|
||||
filters: [],
|
||||
fields: [
|
||||
{
|
||||
fieldMetadataId:
|
||||
messageThreadObjectMetadata.fields.find(
|
||||
(field) =>
|
||||
field.standardId === MESSAGE_THREAD_STANDARD_FIELD_IDS.messages,
|
||||
)?.id ?? '',
|
||||
position: 0,
|
||||
isVisible: true,
|
||||
size: DEFAULT_VIEW_FIELD_SIZE,
|
||||
},
|
||||
{
|
||||
fieldMetadataId:
|
||||
messageThreadObjectMetadata.fields.find(
|
||||
(field) =>
|
||||
field.standardId === BASE_OBJECT_STANDARD_FIELD_IDS.createdAt,
|
||||
)?.id ?? '',
|
||||
position: 1,
|
||||
isVisible: true,
|
||||
size: 150,
|
||||
},
|
||||
],
|
||||
};
|
||||
};
|
||||
+104
@@ -0,0 +1,104 @@
|
||||
import { msg } from '@lingui/core/macro';
|
||||
|
||||
import { type ObjectMetadataEntity } from 'src/engine/metadata-modules/object-metadata/object-metadata.entity';
|
||||
import { DEFAULT_VIEW_FIELD_SIZE } from 'src/engine/workspace-manager/standard-objects-prefill-data/views/constants/DEFAULT_VIEW_FIELD_SIZE';
|
||||
import {
|
||||
BASE_OBJECT_STANDARD_FIELD_IDS,
|
||||
MESSAGE_STANDARD_FIELD_IDS,
|
||||
} from 'src/engine/workspace-manager/workspace-sync-metadata/constants/standard-field-ids';
|
||||
import { STANDARD_OBJECT_IDS } from 'src/engine/workspace-manager/workspace-sync-metadata/constants/standard-object-ids';
|
||||
|
||||
export const messagesAllView = (
|
||||
objectMetadataItems: ObjectMetadataEntity[],
|
||||
useCoreNaming = false,
|
||||
) => {
|
||||
const messageObjectMetadata = objectMetadataItems.find(
|
||||
(object) => object.standardId === STANDARD_OBJECT_IDS.message,
|
||||
);
|
||||
|
||||
if (!messageObjectMetadata) {
|
||||
throw new Error('Message object metadata not found');
|
||||
}
|
||||
|
||||
return {
|
||||
name: useCoreNaming ? msg`All {objectLabelPlural}` : 'All Messages',
|
||||
objectMetadataId: messageObjectMetadata.id ?? '',
|
||||
type: 'table',
|
||||
key: 'INDEX',
|
||||
position: 0,
|
||||
icon: 'IconList',
|
||||
kanbanFieldMetadataId: '',
|
||||
filters: [],
|
||||
fields: [
|
||||
{
|
||||
fieldMetadataId:
|
||||
messageObjectMetadata.fields.find(
|
||||
(field) => field.standardId === MESSAGE_STANDARD_FIELD_IDS.subject,
|
||||
)?.id ?? '',
|
||||
position: 0,
|
||||
isVisible: true,
|
||||
size: DEFAULT_VIEW_FIELD_SIZE,
|
||||
},
|
||||
{
|
||||
fieldMetadataId:
|
||||
messageObjectMetadata.fields.find(
|
||||
(field) =>
|
||||
field.standardId === MESSAGE_STANDARD_FIELD_IDS.messageThread,
|
||||
)?.id ?? '',
|
||||
position: 1,
|
||||
isVisible: true,
|
||||
size: 150,
|
||||
},
|
||||
{
|
||||
fieldMetadataId:
|
||||
messageObjectMetadata.fields.find(
|
||||
(field) =>
|
||||
field.standardId ===
|
||||
MESSAGE_STANDARD_FIELD_IDS.messageParticipants,
|
||||
)?.id ?? '',
|
||||
position: 2,
|
||||
isVisible: true,
|
||||
size: 150,
|
||||
},
|
||||
{
|
||||
fieldMetadataId:
|
||||
messageObjectMetadata.fields.find(
|
||||
(field) =>
|
||||
field.standardId === MESSAGE_STANDARD_FIELD_IDS.receivedAt,
|
||||
)?.id ?? '',
|
||||
position: 3,
|
||||
isVisible: true,
|
||||
size: 150,
|
||||
},
|
||||
{
|
||||
fieldMetadataId:
|
||||
messageObjectMetadata.fields.find(
|
||||
(field) =>
|
||||
field.standardId === MESSAGE_STANDARD_FIELD_IDS.headerMessageId,
|
||||
)?.id ?? '',
|
||||
position: 4,
|
||||
isVisible: true,
|
||||
size: 180,
|
||||
},
|
||||
{
|
||||
fieldMetadataId:
|
||||
messageObjectMetadata.fields.find(
|
||||
(field) => field.standardId === MESSAGE_STANDARD_FIELD_IDS.text,
|
||||
)?.id ?? '',
|
||||
position: 5,
|
||||
isVisible: true,
|
||||
size: 200,
|
||||
},
|
||||
{
|
||||
fieldMetadataId:
|
||||
messageObjectMetadata.fields.find(
|
||||
(field) =>
|
||||
field.standardId === BASE_OBJECT_STANDARD_FIELD_IDS.createdAt,
|
||||
)?.id ?? '',
|
||||
position: 6,
|
||||
isVisible: true,
|
||||
size: 150,
|
||||
},
|
||||
],
|
||||
};
|
||||
};
|
||||
+132
@@ -0,0 +1,132 @@
|
||||
import { msg } from '@lingui/core/macro';
|
||||
|
||||
import { type ObjectMetadataEntity } from 'src/engine/metadata-modules/object-metadata/object-metadata.entity';
|
||||
import { DEFAULT_VIEW_FIELD_SIZE } from 'src/engine/workspace-manager/standard-objects-prefill-data/views/constants/DEFAULT_VIEW_FIELD_SIZE';
|
||||
import {
|
||||
BASE_OBJECT_STANDARD_FIELD_IDS,
|
||||
WORKSPACE_MEMBER_STANDARD_FIELD_IDS,
|
||||
} from 'src/engine/workspace-manager/workspace-sync-metadata/constants/standard-field-ids';
|
||||
import { STANDARD_OBJECT_IDS } from 'src/engine/workspace-manager/workspace-sync-metadata/constants/standard-object-ids';
|
||||
|
||||
export const workspaceMembersAllView = (
|
||||
objectMetadataItems: ObjectMetadataEntity[],
|
||||
useCoreNaming = false,
|
||||
) => {
|
||||
const workspaceMemberObjectMetadata = objectMetadataItems.find(
|
||||
(object) => object.standardId === STANDARD_OBJECT_IDS.workspaceMember,
|
||||
);
|
||||
|
||||
if (!workspaceMemberObjectMetadata) {
|
||||
throw new Error('WorkspaceMember object metadata not found');
|
||||
}
|
||||
|
||||
return {
|
||||
name: useCoreNaming
|
||||
? msg`All {objectLabelPlural}`
|
||||
: 'All Workspace Members',
|
||||
objectMetadataId: workspaceMemberObjectMetadata.id ?? '',
|
||||
type: 'table',
|
||||
key: 'INDEX',
|
||||
position: 0,
|
||||
icon: 'IconList',
|
||||
kanbanFieldMetadataId: '',
|
||||
filters: [],
|
||||
fields: [
|
||||
{
|
||||
fieldMetadataId:
|
||||
workspaceMemberObjectMetadata.fields.find(
|
||||
(field) =>
|
||||
field.standardId === WORKSPACE_MEMBER_STANDARD_FIELD_IDS.name,
|
||||
)?.id ?? '',
|
||||
position: 0,
|
||||
isVisible: true,
|
||||
size: DEFAULT_VIEW_FIELD_SIZE,
|
||||
},
|
||||
{
|
||||
fieldMetadataId:
|
||||
workspaceMemberObjectMetadata.fields.find(
|
||||
(field) =>
|
||||
field.standardId ===
|
||||
WORKSPACE_MEMBER_STANDARD_FIELD_IDS.userEmail,
|
||||
)?.id ?? '',
|
||||
position: 1,
|
||||
isVisible: true,
|
||||
size: 180,
|
||||
},
|
||||
{
|
||||
fieldMetadataId:
|
||||
workspaceMemberObjectMetadata.fields.find(
|
||||
(field) =>
|
||||
field.standardId ===
|
||||
WORKSPACE_MEMBER_STANDARD_FIELD_IDS.avatarUrl,
|
||||
)?.id ?? '',
|
||||
position: 2,
|
||||
isVisible: true,
|
||||
size: 150,
|
||||
},
|
||||
{
|
||||
fieldMetadataId:
|
||||
workspaceMemberObjectMetadata.fields.find(
|
||||
(field) =>
|
||||
field.standardId ===
|
||||
WORKSPACE_MEMBER_STANDARD_FIELD_IDS.colorScheme,
|
||||
)?.id ?? '',
|
||||
position: 3,
|
||||
isVisible: true,
|
||||
size: 150,
|
||||
},
|
||||
{
|
||||
fieldMetadataId:
|
||||
workspaceMemberObjectMetadata.fields.find(
|
||||
(field) =>
|
||||
field.standardId === WORKSPACE_MEMBER_STANDARD_FIELD_IDS.locale,
|
||||
)?.id ?? '',
|
||||
position: 4,
|
||||
isVisible: true,
|
||||
size: 120,
|
||||
},
|
||||
{
|
||||
fieldMetadataId:
|
||||
workspaceMemberObjectMetadata.fields.find(
|
||||
(field) =>
|
||||
field.standardId === WORKSPACE_MEMBER_STANDARD_FIELD_IDS.timeZone,
|
||||
)?.id ?? '',
|
||||
position: 5,
|
||||
isVisible: true,
|
||||
size: 150,
|
||||
},
|
||||
{
|
||||
fieldMetadataId:
|
||||
workspaceMemberObjectMetadata.fields.find(
|
||||
(field) =>
|
||||
field.standardId ===
|
||||
WORKSPACE_MEMBER_STANDARD_FIELD_IDS.dateFormat,
|
||||
)?.id ?? '',
|
||||
position: 6,
|
||||
isVisible: true,
|
||||
size: 120,
|
||||
},
|
||||
{
|
||||
fieldMetadataId:
|
||||
workspaceMemberObjectMetadata.fields.find(
|
||||
(field) =>
|
||||
field.standardId ===
|
||||
WORKSPACE_MEMBER_STANDARD_FIELD_IDS.timeFormat,
|
||||
)?.id ?? '',
|
||||
position: 7,
|
||||
isVisible: true,
|
||||
size: 120,
|
||||
},
|
||||
{
|
||||
fieldMetadataId:
|
||||
workspaceMemberObjectMetadata.fields.find(
|
||||
(field) =>
|
||||
field.standardId === BASE_OBJECT_STANDARD_FIELD_IDS.createdAt,
|
||||
)?.id ?? '',
|
||||
position: 8,
|
||||
isVisible: true,
|
||||
size: 150,
|
||||
},
|
||||
],
|
||||
};
|
||||
};
|
||||
Reference in New Issue
Block a user