From d163600d496afef169c8b3c4874e1f84cd24307d Mon Sep 17 00:00:00 2001 From: Quentin Gliech Date: Sun, 5 Apr 2026 14:56:08 +0200 Subject: [PATCH] refactor(web): introduce colocated GraphQL fragments Define reusable fragments next to the components that consume them: - IdentitySummary: id, humanId, displayName, avatarUrl - LabelFields: name, color { R G B } - BugSummary: composes IdentitySummary + LabelFields for bug list nodes - Timeline fragments: BugCreateCommentFields, BugAddCommentFields, LabelChangeFields, StatusChangeFields, TitleChangeFields Rewrite BugList, BugDetail, and UserProfile queries to compose fragments instead of duplicating field selections. Codegen now includes src/components/**/*.graphql for fragment discovery. Co-Authored-By: Claude Opus 4.6 (1M context) --- webui2/codegen.ts | 2 +- webui2/src/__generated__/graphql.ts | 264 +++++++++--------- .../components/bugs/IdentitySummary.graphql | 6 + webui2/src/components/bugs/IssueRow.graphql | 16 ++ webui2/src/components/bugs/LabelBadge.graphql | 8 + webui2/src/components/bugs/Timeline.graphql | 52 ++++ .../__snapshots__/FileViewer.test.tsx.snap | 230 +++++++++------ webui2/src/graphql/BugDetail.graphql | 84 +----- webui2/src/graphql/BugList.graphql | 23 +- webui2/src/graphql/UserProfile.graphql | 17 +- 10 files changed, 378 insertions(+), 324 deletions(-) create mode 100644 webui2/src/components/bugs/IdentitySummary.graphql create mode 100644 webui2/src/components/bugs/IssueRow.graphql create mode 100644 webui2/src/components/bugs/LabelBadge.graphql create mode 100644 webui2/src/components/bugs/Timeline.graphql diff --git a/webui2/codegen.ts b/webui2/codegen.ts index 1863dc0fe2b795958577d34710d79decf80627e4..cb0e88c41239f57633d7ed4e900700ae78503a0e 100644 --- a/webui2/codegen.ts +++ b/webui2/codegen.ts @@ -2,7 +2,7 @@ import type { CodegenConfig } from "@graphql-codegen/cli"; const config: CodegenConfig = { schema: "../api/graphql/schema/*.graphql", - documents: "src/graphql/**/*.graphql", + documents: ["src/graphql/**/*.graphql", "src/components/**/*.graphql"], generates: { "src/__generated__/graphql.ts": { plugins: ["typescript", "typescript-operations", "typescript-react-apollo"], diff --git a/webui2/src/__generated__/graphql.ts b/webui2/src/__generated__/graphql.ts index 36bf3beb2efc99cb8c5f8df26bdfb2baf07521d3..b7abd2cc0808e78251c1287ea4c8683fe7d0da35 100644 --- a/webui2/src/__generated__/graphql.ts +++ b/webui2/src/__generated__/graphql.ts @@ -1162,6 +1162,22 @@ export type SubscriptionIdentityEventsArgs = { repoRef?: InputMaybe; }; +export type IdentitySummaryFragment = { __typename?: 'Identity', id: string, humanId: string, displayName: string, avatarUrl?: string | null }; + +export type BugSummaryFragment = { __typename?: 'Bug', id: string, humanId: string, status: Status, title: string, createdAt: string, labels: Array<{ __typename?: 'Label', name: string, color: { __typename?: 'Color', R: number, G: number, B: number } }>, author: { __typename?: 'Identity', id: string, humanId: string, displayName: string, avatarUrl?: string | null }, comments: { __typename?: 'BugCommentConnection', totalCount: number } }; + +export type LabelFieldsFragment = { __typename?: 'Label', name: string, color: { __typename?: 'Color', R: number, G: number, B: number } }; + +export type BugCreateCommentFieldsFragment = { __typename?: 'BugCreateTimelineItem', message: string, createdAt: string, lastEdit: string, edited: boolean, author: { __typename?: 'Identity', id: string, humanId: string, displayName: string, avatarUrl?: string | null } }; + +export type BugAddCommentFieldsFragment = { __typename?: 'BugAddCommentTimelineItem', message: string, createdAt: string, lastEdit: string, edited: boolean, author: { __typename?: 'Identity', id: string, humanId: string, displayName: string, avatarUrl?: string | null } }; + +export type LabelChangeFieldsFragment = { __typename?: 'BugLabelChangeTimelineItem', date: string, author: { __typename?: 'Identity', humanId: string, displayName: string }, added: Array<{ __typename?: 'Label', name: string, color: { __typename?: 'Color', R: number, G: number, B: number } }>, removed: Array<{ __typename?: 'Label', name: string, color: { __typename?: 'Color', R: number, G: number, B: number } }> }; + +export type StatusChangeFieldsFragment = { __typename?: 'BugSetStatusTimelineItem', date: string, status: Status, author: { __typename?: 'Identity', humanId: string, displayName: string } }; + +export type TitleChangeFieldsFragment = { __typename?: 'BugSetTitleTimelineItem', date: string, title: string, was: string, author: { __typename?: 'Identity', humanId: string, displayName: string } }; + export type AllIdentitiesQueryVariables = Exact<{ ref?: InputMaybe; }>; @@ -1175,13 +1191,13 @@ export type BugDetailQueryVariables = Exact<{ }>; -export type BugDetailQuery = { __typename?: 'Query', repository?: { __typename?: 'Repository', bug?: { __typename?: 'Bug', id: string, humanId: string, status: Status, title: string, createdAt: string, lastEdit: string, labels: Array<{ __typename?: 'Label', name: string, color: { __typename?: 'Color', R: number, G: number, B: number } }>, author: { __typename?: 'Identity', id: string, humanId: string, displayName: string, avatarUrl?: string | null }, participants: { __typename?: 'IdentityConnection', nodes: Array<{ __typename?: 'Identity', id: string, humanId: string, displayName: string, avatarUrl?: string | null }> }, timeline: { __typename?: 'BugTimelineItemConnection', nodes: Array< - | { __typename: 'BugAddCommentTimelineItem', message: string, createdAt: string, lastEdit: string, edited: boolean, id: string, author: { __typename?: 'Identity', id: string, humanId: string, displayName: string, avatarUrl?: string | null } } - | { __typename: 'BugCreateTimelineItem', message: string, createdAt: string, lastEdit: string, edited: boolean, id: string, author: { __typename?: 'Identity', id: string, humanId: string, displayName: string, avatarUrl?: string | null } } - | { __typename: 'BugLabelChangeTimelineItem', date: string, id: string, author: { __typename?: 'Identity', humanId: string, displayName: string }, added: Array<{ __typename?: 'Label', name: string, color: { __typename?: 'Color', R: number, G: number, B: number } }>, removed: Array<{ __typename?: 'Label', name: string, color: { __typename?: 'Color', R: number, G: number, B: number } }> } - | { __typename: 'BugSetStatusTimelineItem', date: string, status: Status, id: string, author: { __typename?: 'Identity', humanId: string, displayName: string } } - | { __typename: 'BugSetTitleTimelineItem', date: string, title: string, was: string, id: string, author: { __typename?: 'Identity', humanId: string, displayName: string } } - > } } | null } | null }; +export type BugDetailQuery = { __typename?: 'Query', repository?: { __typename?: 'Repository', bug?: { __typename?: 'Bug', lastEdit: string, id: string, humanId: string, status: Status, title: string, createdAt: string, participants: { __typename?: 'IdentityConnection', nodes: Array<{ __typename?: 'Identity', id: string, humanId: string, displayName: string, avatarUrl?: string | null }> }, timeline: { __typename?: 'BugTimelineItemConnection', nodes: Array< + | { __typename: 'BugAddCommentTimelineItem', id: string, message: string, createdAt: string, lastEdit: string, edited: boolean, author: { __typename?: 'Identity', id: string, humanId: string, displayName: string, avatarUrl?: string | null } } + | { __typename: 'BugCreateTimelineItem', id: string, message: string, createdAt: string, lastEdit: string, edited: boolean, author: { __typename?: 'Identity', id: string, humanId: string, displayName: string, avatarUrl?: string | null } } + | { __typename: 'BugLabelChangeTimelineItem', id: string, date: string, author: { __typename?: 'Identity', humanId: string, displayName: string }, added: Array<{ __typename?: 'Label', name: string, color: { __typename?: 'Color', R: number, G: number, B: number } }>, removed: Array<{ __typename?: 'Label', name: string, color: { __typename?: 'Color', R: number, G: number, B: number } }> } + | { __typename: 'BugSetStatusTimelineItem', id: string, date: string, status: Status, author: { __typename?: 'Identity', humanId: string, displayName: string } } + | { __typename: 'BugSetTitleTimelineItem', id: string, date: string, title: string, was: string, author: { __typename?: 'Identity', humanId: string, displayName: string } } + > }, labels: Array<{ __typename?: 'Label', name: string, color: { __typename?: 'Color', R: number, G: number, B: number } }>, author: { __typename?: 'Identity', id: string, humanId: string, displayName: string, avatarUrl?: string | null }, comments: { __typename?: 'BugCommentConnection', totalCount: number } } | null } | null }; export type BugListQueryVariables = Exact<{ ref?: InputMaybe; @@ -1278,7 +1294,7 @@ export type UserProfileQueryVariables = Exact<{ }>; -export type UserProfileQuery = { __typename?: 'Query', repository?: { __typename?: 'Repository', identity?: { __typename?: 'Identity', id: string, humanId: string, name?: string | null, email?: string | null, login?: string | null, displayName: string, avatarUrl?: string | null, isProtected: boolean } | null, openCount: { __typename?: 'BugConnection', totalCount: number }, closedCount: { __typename?: 'BugConnection', totalCount: number }, bugs: { __typename?: 'BugConnection', totalCount: number, pageInfo: { __typename?: 'PageInfo', hasNextPage: boolean, endCursor: string }, nodes: Array<{ __typename?: 'Bug', id: string, humanId: string, status: Status, title: string, createdAt: string, labels: Array<{ __typename?: 'Label', name: string, color: { __typename?: 'Color', R: number, G: number, B: number } }>, comments: { __typename?: 'BugCommentConnection', totalCount: number } }> } } | null }; +export type UserProfileQuery = { __typename?: 'Query', repository?: { __typename?: 'Repository', identity?: { __typename?: 'Identity', id: string, humanId: string, name?: string | null, email?: string | null, login?: string | null, displayName: string, avatarUrl?: string | null, isProtected: boolean } | null, openCount: { __typename?: 'BugConnection', totalCount: number }, closedCount: { __typename?: 'BugConnection', totalCount: number }, bugs: { __typename?: 'BugConnection', totalCount: number, pageInfo: { __typename?: 'PageInfo', hasNextPage: boolean, endCursor: string }, nodes: Array<{ __typename?: 'Bug', id: string, humanId: string, status: Status, title: string, createdAt: string, labels: Array<{ __typename?: 'Label', name: string, color: { __typename?: 'Color', R: number, G: number, B: number } }>, author: { __typename?: 'Identity', id: string, humanId: string, displayName: string, avatarUrl?: string | null }, comments: { __typename?: 'BugCommentConnection', totalCount: number } }> } } | null }; export type ValidLabelsQueryVariables = Exact<{ ref?: InputMaybe; @@ -1287,7 +1303,101 @@ export type ValidLabelsQueryVariables = Exact<{ export type ValidLabelsQuery = { __typename?: 'Query', repository?: { __typename?: 'Repository', validLabels: { __typename?: 'LabelConnection', nodes: Array<{ __typename?: 'Label', name: string, color: { __typename?: 'Color', R: number, G: number, B: number } }> } } | null }; - +export const LabelFieldsFragmentDoc = gql` + fragment LabelFields on Label { + name + color { + R + G + B + } +} + `; +export const IdentitySummaryFragmentDoc = gql` + fragment IdentitySummary on Identity { + id + humanId + displayName + avatarUrl +} + `; +export const BugSummaryFragmentDoc = gql` + fragment BugSummary on Bug { + id + humanId + status + title + labels { + ...LabelFields + } + author { + ...IdentitySummary + } + createdAt + comments { + totalCount + } +} + ${LabelFieldsFragmentDoc} +${IdentitySummaryFragmentDoc}`; +export const BugCreateCommentFieldsFragmentDoc = gql` + fragment BugCreateCommentFields on BugCreateTimelineItem { + author { + ...IdentitySummary + } + message + createdAt + lastEdit + edited +} + ${IdentitySummaryFragmentDoc}`; +export const BugAddCommentFieldsFragmentDoc = gql` + fragment BugAddCommentFields on BugAddCommentTimelineItem { + author { + ...IdentitySummary + } + message + createdAt + lastEdit + edited +} + ${IdentitySummaryFragmentDoc}`; +export const LabelChangeFieldsFragmentDoc = gql` + fragment LabelChangeFields on BugLabelChangeTimelineItem { + author { + humanId + displayName + } + date + added { + ...LabelFields + } + removed { + ...LabelFields + } +} + ${LabelFieldsFragmentDoc}`; +export const StatusChangeFieldsFragmentDoc = gql` + fragment StatusChangeFields on BugSetStatusTimelineItem { + author { + humanId + displayName + } + date + status +} + `; +export const TitleChangeFieldsFragmentDoc = gql` + fragment TitleChangeFields on BugSetTitleTimelineItem { + author { + humanId + displayName + } + date + title + was +} + `; export const AllIdentitiesDocument = gql` query AllIdentities($ref: String) { repository(ref: $ref) { @@ -1345,32 +1455,11 @@ export const BugDetailDocument = gql` query BugDetail($ref: String, $prefix: String!) { repository(ref: $ref) { bug(prefix: $prefix) { - id - humanId - status - title - labels { - name - color { - R - G - B - } - } - author { - id - humanId - displayName - avatarUrl - } - createdAt + ...BugSummary lastEdit participants(first: 20) { nodes { - id - humanId - displayName - avatarUrl + ...IdentitySummary } } timeline(first: 250) { @@ -1378,75 +1467,32 @@ export const BugDetailDocument = gql` __typename id ... on BugCreateTimelineItem { - author { - id - humanId - displayName - avatarUrl - } - message - createdAt - lastEdit - edited + ...BugCreateCommentFields } ... on BugAddCommentTimelineItem { - author { - id - humanId - displayName - avatarUrl - } - message - createdAt - lastEdit - edited + ...BugAddCommentFields } ... on BugLabelChangeTimelineItem { - author { - humanId - displayName - } - date - added { - name - color { - R - G - B - } - } - removed { - name - color { - R - G - B - } - } + ...LabelChangeFields } ... on BugSetStatusTimelineItem { - author { - humanId - displayName - } - date - status + ...StatusChangeFields } ... on BugSetTitleTimelineItem { - author { - humanId - displayName - } - date - title - was + ...TitleChangeFields } } } } } } - `; + ${BugSummaryFragmentDoc} +${IdentitySummaryFragmentDoc} +${BugCreateCommentFieldsFragmentDoc} +${BugAddCommentFieldsFragmentDoc} +${LabelChangeFieldsFragmentDoc} +${StatusChangeFieldsFragmentDoc} +${TitleChangeFieldsFragmentDoc}`; /** * __useBugDetailQuery__ @@ -1500,33 +1546,12 @@ export const BugListDocument = gql` endCursor } nodes { - id - humanId - status - title - labels { - name - color { - R - G - B - } - } - author { - id - humanId - displayName - avatarUrl - } - createdAt - comments { - totalCount - } + ...BugSummary } } } } - `; + ${BugSummaryFragmentDoc}`; /** * __useBugListQuery__ @@ -2009,27 +2034,12 @@ export const UserProfileDocument = gql` endCursor } nodes { - id - humanId - status - title - labels { - name - color { - R - G - B - } - } - createdAt - comments { - totalCount - } + ...BugSummary } } } } - `; + ${BugSummaryFragmentDoc}`; /** * __useUserProfileQuery__ diff --git a/webui2/src/components/bugs/IdentitySummary.graphql b/webui2/src/components/bugs/IdentitySummary.graphql new file mode 100644 index 0000000000000000000000000000000000000000..7c7c81dcea774f39e1a30de9c7e111faa25983d8 --- /dev/null +++ b/webui2/src/components/bugs/IdentitySummary.graphql @@ -0,0 +1,6 @@ +fragment IdentitySummary on Identity { + id + humanId + displayName + avatarUrl +} diff --git a/webui2/src/components/bugs/IssueRow.graphql b/webui2/src/components/bugs/IssueRow.graphql new file mode 100644 index 0000000000000000000000000000000000000000..dfc1c8fd978869176aa1b9fba94cc6d0212715e7 --- /dev/null +++ b/webui2/src/components/bugs/IssueRow.graphql @@ -0,0 +1,16 @@ +fragment BugSummary on Bug { + id + humanId + status + title + labels { + ...LabelFields + } + author { + ...IdentitySummary + } + createdAt + comments { + totalCount + } +} diff --git a/webui2/src/components/bugs/LabelBadge.graphql b/webui2/src/components/bugs/LabelBadge.graphql new file mode 100644 index 0000000000000000000000000000000000000000..bc63f2e689c2f44fd115d260d9bcce560d747ccf --- /dev/null +++ b/webui2/src/components/bugs/LabelBadge.graphql @@ -0,0 +1,8 @@ +fragment LabelFields on Label { + name + color { + R + G + B + } +} diff --git a/webui2/src/components/bugs/Timeline.graphql b/webui2/src/components/bugs/Timeline.graphql new file mode 100644 index 0000000000000000000000000000000000000000..dd718cc1e3fcbc1e4d40cb2f15e32d57bdce2d57 --- /dev/null +++ b/webui2/src/components/bugs/Timeline.graphql @@ -0,0 +1,52 @@ +fragment BugCreateCommentFields on BugCreateTimelineItem { + author { + ...IdentitySummary + } + message + createdAt + lastEdit + edited +} + +fragment BugAddCommentFields on BugAddCommentTimelineItem { + author { + ...IdentitySummary + } + message + createdAt + lastEdit + edited +} + +fragment LabelChangeFields on BugLabelChangeTimelineItem { + author { + humanId + displayName + } + date + added { + ...LabelFields + } + removed { + ...LabelFields + } +} + +fragment StatusChangeFields on BugSetStatusTimelineItem { + author { + humanId + displayName + } + date + status +} + +fragment TitleChangeFields on BugSetTitleTimelineItem { + author { + humanId + displayName + } + date + title + was +} diff --git a/webui2/src/components/code/__snapshots__/FileViewer.test.tsx.snap b/webui2/src/components/code/__snapshots__/FileViewer.test.tsx.snap index 222be18d9d9b2f7204fc1b4706a4175e0a2fe685..8727a6755a74eb9497c9283c64d04da0f708505b 100644 --- a/webui2/src/components/code/__snapshots__/FileViewer.test.tsx.snap +++ b/webui2/src/components/code/__snapshots__/FileViewer.test.tsx.snap @@ -83,95 +83,163 @@ exports[`FileViewer/TruncatedFile matches snapshot 1`] = ` class="border-border overflow-hidden rounded-md border" >
- - 4 - lines · - 1.0 MB - · truncated - - +