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
@@ -103,6 +103,7 @@ export type LinksFilter = {
export type ActorFilter = {
name?: StringFilter;
source?: SelectFilter;
workspaceMemberId?: UUIDFilter;
};
export type EmailsFilter = {
@@ -1153,6 +1153,71 @@ export const turnRecordFilterIntoRecordGqlOperationFilter = ({
}
}
if (subFieldName === 'workspaceMemberId') {
const { isCurrentWorkspaceMemberSelected, selectedRecordIds } =
jsonRelationFilterValueSchema
.catch({
isCurrentWorkspaceMemberSelected: false,
selectedRecordIds: arrayOfUuidOrVariableSchema.parse(
recordFilter.value,
),
})
.parse(recordFilter.value);
const workspaceMemberIds = isCurrentWorkspaceMemberSelected
? [
...selectedRecordIds,
filterValueDependencies?.currentWorkspaceMemberId,
].filter(isDefined)
: selectedRecordIds;
if (!isDefined(workspaceMemberIds) || workspaceMemberIds.length === 0) {
return;
}
switch (recordFilter.operand) {
case RecordFilterOperand.IS:
return {
[correspondingFieldMetadataItem.name]: {
workspaceMemberId: {
in: workspaceMemberIds,
} satisfies UUIDFilter,
},
};
case RecordFilterOperand.IS_NOT: {
return {
or: [
{
not: {
[correspondingFieldMetadataItem.name]: {
workspaceMemberId: {
in: workspaceMemberIds,
} satisfies UUIDFilter,
},
},
},
{
[correspondingFieldMetadataItem.name]: {
workspaceMemberId: {
is: 'NULL',
} satisfies UUIDFilter,
},
},
],
};
}
default: {
const fieldForRecordFilter = fieldMetadataItems.find(
(field) => field.id === recordFilter.fieldMetadataId,
);
throw new Error(
`Unknown operand ${recordFilter.operand} for ${fieldForRecordFilter?.label ?? ''} filter`,
);
}
}
}
switch (recordFilter.operand) {
case RecordFilterOperand.CONTAINS:
return {