✨ Fix refresh + add CNCF project icons on files in Mission Explorer#4071
✨ Fix refresh + add CNCF project icons on files in Mission Explorer#4071clubanderson merged 2 commits intomainfrom
Conversation
- Remove source, PR, and delete buttons from file rows in tree sidebar (delete stays on repo-level directory nodes only) - Add Source and PR buttons in the MissionDetailView header alongside Share/View Raw/Import — opens GitHub view/edit in new tab - Pass githubSourceUrl and githubEditUrl from MissionBrowser to detail view Signed-off-by: Andrew Anderson <andy@clubanderson.com>
Refresh button: - Rewrote onRefresh to fetch contents directly via GitHub Contents API instead of going through toggleNode (which had stale closure issues causing it to just collapse/expand without re-fetching) - Shows loading spinner during fetch, keeps node expanded Project icons on files: - Detects CNCF project from filename using regex patterns (argo, flux, karmada, prometheus, cert-manager, istio, ray, etc.) - Shows GitHub org avatar (32px) as the file icon when a project match is found, falls back to generic FileCode/FileText/FileJson icons Signed-off-by: Andrew Anderson <andy@clubanderson.com>
|
[APPROVALNOTIFIER] This PR is NOT APPROVED This pull-request has been approved by: The full list of commands accepted by this bot can be found here. DetailsNeeds approval from an approver in each of these files:Approvers can indicate their approval by writing |
✅ Deploy Preview for kubestellarconsole ready!
To edit notification comments on pull requests, go to your Netlify project configuration. |
|
👋 Hey @clubanderson — thanks for opening this PR!
This is an automated message. |
|
Thank you for your contribution! Your PR has been merged. Check out what's new:
Stay connected: Slack #kubestellar-dev | Multi-Cluster Survey |
There was a problem hiding this comment.
Pull request overview
Updates the Mission Explorer UI to improve GitHub tree refresh behavior and enrich file nodes with CNCF project visual cues, while also exposing GitHub “Source” and “PR” links from the mission detail panel.
Changes:
- Reworked the tree node refresh handler to fetch GitHub contents directly (avoiding
toggleNodestale-closure behavior) and keep a loading spinner active during refresh. - Added GitHub “Source” and “PR” buttons to
MissionDetailView, derived from the selected GitHub path. - Added filename-based CNCF project detection to display GitHub org avatar icons for matching files in the tree.
Reviewed changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated 4 comments.
| File | Description |
|---|---|
| web/src/components/missions/MissionDetailView.tsx | Adds optional GitHub Source/Edit URLs and renders header links. |
| web/src/components/missions/MissionBrowser.tsx | Refactors refresh to direct GitHub Contents API calls; derives Source/Edit URLs from selected GitHub node id. |
| web/src/components/missions/browser/TreeNodeItem.tsx | Adds filename→CNCF org detection and renders GitHub avatar icons for matching files; removes per-file GitHub action buttons. |
| // Fetch fresh contents directly (bypass toggleNode to avoid stale closure issues) | ||
| let children: TreeNode[] = [] | ||
| if (child.source === 'github' && child.id !== 'github') { | ||
| const repoPath = child.path | ||
| const { data: ghEntries } = await api.get<Array<{ name: string; path: string; type: string; size?: number }>>( | ||
| `/api/github/repos/${repoPath}/contents` | ||
| ) | ||
| children = (ghEntries || []) | ||
| .filter(e => e.type === 'dir' || isMissionFile(e.name)) | ||
| .map(e => ({ | ||
| id: `${child.id}/${e.name}`, | ||
| name: e.name, | ||
| path: `${repoPath.split('/').slice(0, 2).join('/')}/${e.path}`, | ||
| type: (e.type === 'dir' ? 'directory' : 'file') as TreeNode['type'], | ||
| source: 'github' as const, | ||
| loaded: e.type !== 'dir', | ||
| })) | ||
| } |
There was a problem hiding this comment.
onRefresh is wired up for both GitHub and Local nodes, but the implementation only re-fetches children for child.source === 'github'. For local watched paths this will always set children to [] and mark the node as empty/loaded, effectively wiping the listing. Either implement a local re-scan here (matching the intended behavior in the prop doc) or restrict onRefresh to GitHub until local browsing is supported.
| } catch { | ||
| setTreeNodes((prev) => | ||
| updateNodeInTree(prev, child.id, { children: [], loaded: true, loading: false, isEmpty: true }) | ||
| ) |
There was a problem hiding this comment.
The refresh catch path marks the node as loaded: true + isEmpty: true and clears children, but it drops the more descriptive failure marker used in toggleNode (e.g., 'Failed to load — check network or GitHub rate limits'). Consider preserving existing children on failure and/or setting a description (and possibly a toast) so users can tell a refresh failed instead of thinking the folder is empty.
| const [owner, repo, ...rest] = parts | ||
| const filePath = rest.join('/') | ||
| return { | ||
| githubSourceUrl: `https://github.com/${owner}/${repo}/blob/main/${filePath}`, | ||
| githubEditUrl: `https://github.com/${owner}/${repo}/edit/main/${filePath}`, | ||
| } |
There was a problem hiding this comment.
The generated GitHub URLs hard-code main (/blob/main/... and /edit/main/...). This will be broken for repos whose default branch is not main (e.g., master, trunk, or a custom branch). Consider deriving the default branch from the GitHub API response (repo metadata) and using that here, or omit the links when the branch can’t be determined reliably.
| const [owner, repo, ...rest] = parts | |
| const filePath = rest.join('/') | |
| return { | |
| githubSourceUrl: `https://github.com/${owner}/${repo}/blob/main/${filePath}`, | |
| githubEditUrl: `https://github.com/${owner}/${repo}/edit/main/${filePath}`, | |
| } | |
| // We cannot reliably determine the repository's default branch here, | |
| // so avoid constructing GitHub URLs that might be broken. | |
| return {} |
| <img | ||
| src={`https://github.com/${projectOrg}.png?size=32`} | ||
| alt={projectOrg} | ||
| className="w-4 h-4 rounded-sm flex-shrink-0" | ||
| onError={(e) => { (e.target as HTMLImageElement).style.display = 'none' }} | ||
| /> |
There was a problem hiding this comment.
If the GitHub avatar request fails, the onError handler hides the <img> but doesn’t render a fallback icon, so the file ends up with no icon at all. It would be more robust to fall back to the existing generic file-type icon (YAML/MD/JSON) when the avatar can’t be loaded (and optionally add loading="lazy" / decoding="async" to reduce the impact of many avatar requests in large trees).
🔄 Auto-Applying Copilot Code ReviewCopilot code review found 1 code suggestion(s) and 3 general comment(s). @copilot Please apply all of the following code review suggestions:
Also address these general comments:
Push all fixes in a single commit. Run Auto-generated by copilot-review-apply workflow. |
Summary
Refresh button fix:
onRefreshto call GitHub Contents API directly instead of going throughtoggleNode(which had stale closure issues causing collapse/expand without re-fetching)CNCF project icons:
argocd-application.yaml→ Argo CD logo)Test plan
argocd-application.yamlshows Argo CD avatar iconfluxcd-helmrelease.yamlshows FluxCD avatar iconkarmada-propagation.yamlshows Karmada avatar iconray-cluster.yamlshows Ray Project avatar iconmulti-project.yamlhas no specific project match → generic orange YAML icon