feat: added workspace member filter for actor fields (#16628)

Closes #16619

This PR adds support for filtering ACTOR fields by `Workspace Member`
subfield, enabling users to filter records by who created them with a
`Me` option.

I replicated the same UX and code structure as the Relation field filter
for consistency.

Each filter selection triggers a server-side GraphQL call. But there is
client-side filtering in `isRecordMatchingFilter.ts` which instantly
filters already-loaded records while the server is fetching new results.
I replicated this behaviour from how `FULL_NAME` and other composite
fields handle filtering.


UX decision that were taken by me (Let me know if changes are needed) : 
- The filter shows comma separated selected workspace member names until
3 members are selected. After that it shows [no of selected members]
workspace members.

<img width="643" height="283" alt="Screenshot 2025-12-17 at 8 01 23 PM"
src="https://github.com/user-attachments/assets/18d524f4-979b-4d92-ad64-aa7b63be7897"
/>
<img width="774" height="309" alt="Screenshot 2025-12-17 at 8 01 33 PM"
src="https://github.com/user-attachments/assets/779f374f-1501-48f9-afa2-a78628e067b1"
/>
<img width="737" height="397" alt="Screenshot 2025-12-17 at 8 01 40 PM"
src="https://github.com/user-attachments/assets/4e8b963e-8a0f-467d-91ae-48bca4e1d842"
/>
<img width="697" height="334" alt="Screenshot 2025-12-17 at 8 01 53 PM"
src="https://github.com/user-attachments/assets/c9a0e4bb-48b4-486b-a4f9-d7c49ff3852c"
/>
<img width="684" height="343" alt="Screenshot 2025-12-17 at 8 02 04 PM"
src="https://github.com/user-attachments/assets/d5c45eba-06a3-40fc-b9d0-d585dc8bc136"
/>

---------

Co-authored-by: Lucas Bordeau <bordeau.lucas@gmail.com>
This commit is contained in:
Abhishek Kumar
2026-02-19 22:26:40 +05:30
committed by GitHub
parent 2a670520b9
commit aaa6511007
21 changed files with 413 additions and 33 deletions
@@ -1,6 +1,11 @@
import { isDefined } from 'twenty-shared/utils';
import { COMPANY_DATA_SEED_IDS } from 'src/engine/workspace-manager/dev-seeder/data/constants/company-data-seeds.constant';
import { PERSON_DATA_SEED_IDS } from 'src/engine/workspace-manager/dev-seeder/data/constants/person-data-seeds.constant';
import { WORKSPACE_MEMBER_DATA_SEED_IDS } from 'src/engine/workspace-manager/dev-seeder/data/constants/workspace-member-data-seeds.constant';
import {
WORKSPACE_MEMBER_DATA_SEED_IDS,
WORKSPACE_MEMBER_DATA_SEEDS,
} from 'src/engine/workspace-manager/dev-seeder/data/constants/workspace-member-data-seeds.constant';
type OpportunityDataSeed = {
id: string;
@@ -177,7 +182,17 @@ const GENERATE_OPPORTUNITY_SEEDS = (): OpportunityDataSeed[] => {
CLOSE_DATE.setDate(CLOSE_DATE.getDate() + DAYS_AHEAD);
OPPORTUNITY_SEEDS.push({
const workspaceMemberId = Object.values(WORKSPACE_MEMBER_DATA_SEED_IDS)[
INDEX % 4
];
const workspaceMember = WORKSPACE_MEMBER_DATA_SEEDS.find(
(workspaceMember) => workspaceMember.id === workspaceMemberId,
);
const workspaceMemberName = isDefined(workspaceMember)
? `${workspaceMember?.nameFirstName} ${workspaceMember?.nameLastName}`
: 'Unkonwn';
const rawSeed: OpportunityDataSeed = {
id: OPPORTUNITY_DATA_SEED_IDS[`ID_${INDEX}`],
name: TEMPLATE.name,
amountAmountMicros: TEMPLATE.amount * 1000000,
@@ -195,12 +210,22 @@ const GENERATE_OPPORTUNITY_SEEDS = (): OpportunityDataSeed[] => {
] || COMPANY_DATA_SEED_IDS.ID_1,
ownerId: WORKSPACE_MEMBER_DATA_SEED_IDS.TIM,
createdBySource: 'MANUAL',
createdByWorkspaceMemberId: WORKSPACE_MEMBER_DATA_SEED_IDS.TIM,
createdByName: 'Tim Cook',
updatedBySource: 'MANUAL',
updatedByWorkspaceMemberId: WORKSPACE_MEMBER_DATA_SEED_IDS.TIM,
updatedByName: 'Tim Cook',
});
createdByWorkspaceMemberId: workspaceMemberId,
createdByName: workspaceMemberName,
updatedByWorkspaceMemberId: workspaceMemberId,
updatedByName: workspaceMemberName,
};
const opportunityDataSeedWithSQLColumnOrder: OpportunityDataSeed =
Object.fromEntries(
OPPORTUNITY_DATA_SEED_COLUMNS.map((column) => [
column,
rawSeed[column as keyof OpportunityDataSeed],
]),
) as OpportunityDataSeed;
OPPORTUNITY_SEEDS.push(opportunityDataSeedWithSQLColumnOrder);
}
return OPPORTUNITY_SEEDS;
@@ -1,5 +1,10 @@
import { isDefined } from 'twenty-shared/utils';
import { COMPANY_DATA_SEED_IDS } from 'src/engine/workspace-manager/dev-seeder/data/constants/company-data-seeds.constant';
import { WORKSPACE_MEMBER_DATA_SEED_IDS } from 'src/engine/workspace-manager/dev-seeder/data/constants/workspace-member-data-seeds.constant';
import {
WORKSPACE_MEMBER_DATA_SEED_IDS,
WORKSPACE_MEMBER_DATA_SEEDS,
} from 'src/engine/workspace-manager/dev-seeder/data/constants/workspace-member-data-seeds.constant';
type PersonDataSeed = {
id: string;
@@ -11,12 +16,12 @@ type PersonDataSeed = {
linkedinLinkPrimaryLinkUrl: string;
jobTitle: string;
companyId: string;
createdBySource: string;
createdByWorkspaceMemberId: string;
createdByName: string;
phonesPrimaryPhoneNumber: string;
phonesPrimaryPhoneCountryCode: string;
phonesPrimaryPhoneCallingCode: string;
createdBySource: string;
createdByWorkspaceMemberId: string;
createdByName: string;
updatedBySource: string;
updatedByWorkspaceMemberId: string;
updatedByName: string;
@@ -33,12 +38,12 @@ export const PERSON_DATA_SEED_COLUMNS: (keyof PersonDataSeed)[] = [
'linkedinLinkPrimaryLinkUrl',
'jobTitle',
'companyId',
'createdBySource',
'createdByWorkspaceMemberId',
'createdByName',
'phonesPrimaryPhoneNumber',
'phonesPrimaryPhoneCountryCode',
'phonesPrimaryPhoneCallingCode',
'createdBySource',
'createdByWorkspaceMemberId',
'createdByName',
'updatedBySource',
'updatedByWorkspaceMemberId',
'updatedByName',
@@ -21654,13 +21659,37 @@ const PERSON_DATA_SEEDS_RAW = [
];
export const PERSON_DATA_SEEDS: PersonDataSeed[] = PERSON_DATA_SEEDS_RAW.map(
(person, index) => ({
...person,
updatedBySource: person.createdBySource,
updatedByWorkspaceMemberId: person.createdByWorkspaceMemberId,
updatedByName: person.createdByName,
position: index + 1,
}),
(person, index) => {
const workspaceMemberId = Object.values(WORKSPACE_MEMBER_DATA_SEED_IDS)[
index % 4
];
const workspaceMember = WORKSPACE_MEMBER_DATA_SEEDS.find(
(workspaceMember) => workspaceMember.id === workspaceMemberId,
);
const workspaceMemberName = isDefined(workspaceMember)
? `${workspaceMember?.nameFirstName} ${workspaceMember?.nameLastName}`
: 'Unkonwn';
const dataSeed: PersonDataSeed = {
...person,
createdBySource: person.createdBySource,
createdByWorkspaceMemberId: workspaceMemberId,
createdByName: workspaceMemberName,
updatedBySource: person.createdBySource,
updatedByWorkspaceMemberId: workspaceMemberId,
updatedByName: workspaceMemberName,
position: index + 1,
};
const personDataSeedWithSQLColumnOrder: PersonDataSeed = Object.fromEntries(
PERSON_DATA_SEED_COLUMNS.map((column) => [
column,
dataSeed[column as keyof PersonDataSeed],
]),
) as PersonDataSeed;
return personDataSeedWithSQLColumnOrder;
},
);
// Map for O(1) lookups by person ID