From 5abe6a863550d499b99d143db2de5ccb6ccbded5 Mon Sep 17 00:00:00 2001 From: Ulrik Andersen Date: Wed, 10 Dec 2025 18:46:59 +0100 Subject: [PATCH] Add has-changes indicator for specs in PR branches Show a visual indicator (orange dot with tooltip) on specs that have been modified in the current PR. Only specs whose filenames appear in the PR's changed files list will display the indicator. - Fetch changed files from PR via GraphQL - Add changedFiles to GitHubRepositoryRef type - Only set diffURL on specs in changedFiles - Display subtle orange dot with "Has changes" tooltip --- .../projects/data/GitHubProjectDataSource.ts | 11 +++--- .../data/GitHubRepositoryDataSource.ts | 24 +++++++++++-- .../domain/IGitHubRepositoryDataSource.ts | 5 +-- .../projects/view/toolbar/Selector.tsx | 35 ++++++++++++++----- 4 files changed, 55 insertions(+), 20 deletions(-) diff --git a/src/features/projects/data/GitHubProjectDataSource.ts b/src/features/projects/data/GitHubProjectDataSource.ts index f8e1f8d8..4762ed3e 100644 --- a/src/features/projects/data/GitHubProjectDataSource.ts +++ b/src/features/projects/data/GitHubProjectDataSource.ts @@ -122,6 +122,7 @@ export default class GitHubProjectDataSource implements IProjectDataSource { const specifications = ref.files.filter(file => { return this.isOpenAPISpecification(file.name) }).map(file => { + const isFileChanged = ref.changedFiles?.includes(file.name) ?? false return { id: file.name, name: file.name, @@ -132,16 +133,16 @@ export default class GitHubProjectDataSource implements IProjectDataSource { ref: ref.id }), editURL: `https://github.com/${ownerName}/${repositoryName}/edit/${ref.name}/${encodeURIComponent(file.name)}`, - diffURL: this.getGitHubDiffURL({ + diffURL: isFileChanged ? this.getGitHubDiffURL({ ownerName, repositoryName, path: file.name, baseRefOid: ref.baseRefOid, headRefOid: ref.id - }), - diffBaseBranch: ref.baseRef, - diffBaseOid: ref.baseRefOid, - diffPrUrl: ref.prNumber ? `https://github.com/${ownerName}/${repositoryName}/pull/${ref.prNumber}` : undefined, + }) : undefined, + diffBaseBranch: isFileChanged ? ref.baseRef : undefined, + diffBaseOid: isFileChanged ? ref.baseRefOid : undefined, + diffPrUrl: isFileChanged && ref.prNumber ? `https://github.com/${ownerName}/${repositoryName}/pull/${ref.prNumber}` : undefined, isDefault: false // initial value } }).sort((a, b) => a.name.localeCompare(b.name)) diff --git a/src/features/projects/data/GitHubRepositoryDataSource.ts b/src/features/projects/data/GitHubRepositoryDataSource.ts index c107b506..6a776e34 100644 --- a/src/features/projects/data/GitHubRepositoryDataSource.ts +++ b/src/features/projects/data/GitHubRepositoryDataSource.ts @@ -51,6 +51,7 @@ type GraphQLPullRequest = { readonly headRefName: string readonly baseRefName: string readonly baseRefOid: string + readonly changedFiles: string[] } export default class GitHubProjectDataSource implements IGitHubRepositoryDataSource { @@ -129,7 +130,8 @@ export default class GitHubProjectDataSource implements IGitHubRepositoryDataSou baseRef: pr?.baseRefName, baseRefOid: pr?.baseRefOid, prNumber: pr?.number, - files: branch.node.target.tree.entries + files: branch.node.target.tree.entries, + changedFiles: pr?.changedFiles } }) @@ -174,6 +176,11 @@ export default class GitHubProjectDataSource implements IGitHubRepositoryDataSou headRefName baseRefName baseRefOid + files(first: 100) { + nodes { + path + } + } } } } @@ -199,15 +206,26 @@ export default class GitHubProjectDataSource implements IGitHubRepositoryDataSou const pullRequests = new Map() if (repoData?.pullRequests?.edges) { - const pullRequestEdges = repoData.pullRequests.edges as Edge[] + type RawGraphQLPullRequest = { + number: number + headRefName: string + baseRefName: string + baseRefOid: string + files?: { + nodes?: { path: string }[] + } + } + const pullRequestEdges = repoData.pullRequests.edges as Edge[] pullRequestEdges.forEach(edge => { const pr = edge.node + const changedFiles = pr.files?.nodes?.map(f => f.path) || [] pullRequests.set(pr.headRefName, { number: pr.number, headRefName: pr.headRefName, baseRefName: pr.baseRefName, - baseRefOid: pr.baseRefOid + baseRefOid: pr.baseRefOid, + changedFiles }) }) } diff --git a/src/features/projects/domain/IGitHubRepositoryDataSource.ts b/src/features/projects/domain/IGitHubRepositoryDataSource.ts index eb331a95..c9c86e4e 100644 --- a/src/features/projects/domain/IGitHubRepositoryDataSource.ts +++ b/src/features/projects/domain/IGitHubRepositoryDataSource.ts @@ -24,10 +24,7 @@ export type GitHubRepositoryRef = { readonly files: { readonly name: string }[] -} - -export default interface IGitHubRepositoryDataSource { - getRepositories(): Promise + readonly changedFiles?: string[] } export default interface IGitHubRepositoryDataSource { diff --git a/src/features/projects/view/toolbar/Selector.tsx b/src/features/projects/view/toolbar/Selector.tsx index 4607d1f0..91261998 100644 --- a/src/features/projects/view/toolbar/Selector.tsx +++ b/src/features/projects/view/toolbar/Selector.tsx @@ -4,13 +4,16 @@ import { MenuItem, SelectChangeEvent, FormControl, - Typography + Typography, + Box, + Tooltip } from "@mui/material" import MenuItemHover from "@/common/ui/MenuItemHover" interface SelectorItem { readonly id: string readonly name: string + readonly hasChanges?: boolean } const Selector = ({ @@ -45,13 +48,29 @@ const Selector = ({ {items.map(item => ( - - {item.name} - + + + {item.name} + + {item.hasChanges && ( + + + + )} + ))}