feat: custom fields per project
Text, number, date or option-list fields defined in the project settings (archived, never removed; their type is fixed once saved). Tickets hold them, edited in the side panel, listed under the title and filtered by option in the list view; also over GraphQL, REST (?field=ID:value) and MCP. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -43,6 +43,7 @@ import {
|
||||
CommandResultGql,
|
||||
CommentPageGql,
|
||||
EpicGql,
|
||||
CustomFieldValueGql,
|
||||
IssueGql,
|
||||
IssuePageGql,
|
||||
IssueParentGql,
|
||||
@@ -75,8 +76,10 @@ export class IssueResolver {
|
||||
@Args('limit', { type: () => Int, defaultValue: 50 }) limit: number,
|
||||
@Args('offset', { type: () => Int, defaultValue: 0 }) offset: number,
|
||||
) {
|
||||
const { customFields, ...rest } = filter;
|
||||
return this.issuesService.search(viewerOf(me), {
|
||||
...filter,
|
||||
...rest,
|
||||
customFields: onlyValues(customFields),
|
||||
limit,
|
||||
offset,
|
||||
});
|
||||
@@ -150,6 +153,16 @@ export class IssueResolver {
|
||||
});
|
||||
}
|
||||
|
||||
@ResolveField(() => [CustomFieldValueGql], {
|
||||
description: "Values of the project's custom fields (only those set)",
|
||||
})
|
||||
customFields(@Parent() issue: IssueReadModel): CustomFieldValueGql[] {
|
||||
return Object.entries(issue.customFields ?? {}).map(([fieldId, value]) => ({
|
||||
fieldId,
|
||||
value,
|
||||
}));
|
||||
}
|
||||
|
||||
@ResolveField(() => [String], {
|
||||
description: "Statuses this ticket can move to next (project's workflow)",
|
||||
})
|
||||
@@ -432,3 +445,13 @@ export class IssueResolver {
|
||||
return this.issuesService.setRelease(me.id, issueId, releaseId ?? null);
|
||||
}
|
||||
}
|
||||
|
||||
/** A filter's custom values: the ones that name something to match. */
|
||||
function onlyValues(
|
||||
values: { fieldId: string; value?: string | null }[] | undefined,
|
||||
): Record<string, string> | undefined {
|
||||
const pairs = (values ?? []).filter((v) => v.value);
|
||||
return pairs.length
|
||||
? Object.fromEntries(pairs.map((v) => [v.fieldId, v.value as string]))
|
||||
: undefined;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user