-
Notifications
You must be signed in to change notification settings - Fork 9
feat: enhance unclaimed nodes modal with GitHub repository info fetching #204
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
🎨 Chromatic Visual Testing Results
Check the visual changes and approve or request changes as needed. |
1177152 to
9d9c358
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull Request Overview
This PR enhances the admin unclaimed nodes modal by adding GitHub repository integration that automatically fetches and populates project metadata from pyproject.toml files. The key improvement is a streamlined user experience where administrators can enter a GitHub URL and click "Fetch Info" to auto-populate form fields, reducing manual data entry and potential errors.
Key Changes
- Implemented GitHub API integration to fetch
pyproject.tomlcontent and parse project metadata (name, description, author, license) - Repositioned repository URL field to the top of the form with accompanying fetch button for better UX
- Added comprehensive translation support across 8 languages (English, Spanish, French, Japanese, Korean, Chinese, Russian, Turkish, Arabic)
Reviewed Changes
Copilot reviewed 12 out of 12 changed files in this pull request and generated 6 comments.
| File | Description |
|---|---|
| components/nodes/AdminCreateNodeFormModal.tsx | Added fetchGitHubRepoInfo function, fetch button with loading states, and form auto-population logic |
| locales/*/common.json | Added translations for new UI strings (Repository URL, Fetch Info, success/error messages, helper text) |
| src/stories/components/nodes/AdminCreateNodeFormModal.stories.tsx | Created new Storybook story with documentation and example scenarios |
| .storybook/preview.tsx | Cleaned up preview configuration by removing unused code and simplifying locale setup |
Tip: Customize your code reviews with copilot-instructions.md. Create the file or learn how to get started.
src/stories/components/nodes/AdminCreateNodeFormModal.stories.tsx
Outdated
Show resolved
Hide resolved
- Move repository URL field to top of the modal for better UX - Add "Fetch Info" button to automatically populate form fields from GitHub pyproject.toml - Implement GitHub API integration to extract name, description, author, and license - Add comprehensive error handling and user feedback with toast notifications - Create Storybook story for the enhanced modal component - Include helpful placeholder text and loading states for better user experience 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
…DisplayNames calls The about panel in Storybook was showing a blank screen due to the languageName function using Intl.DisplayNames at module load time. Replaced dynamic language name generation with static, hardcoded language names in the locale toolbar configuration to ensure reliable initialization. Also applied code formatting fixes across locale files and other source files. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
- Fix Spanish translations in locales/es/common.json (lines 155, 259) - Fix French translation in locales/fr/common.json (line 154) - Replace 'any' type with React.ComponentProps in AdminCreateNodeFormModal.stories.tsx - Add base64 validation before atob() in AdminCreateNodeFormModal.tsx - Replace regex TOML parsing with proper smol-toml library for robust parsing - Fix prefer-template ESLint warning 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
6271940 to
fe694aa
Compare
- Fix AdminCreateNodeFormModal.tsx duplicate code from merge - Fix .storybook/preview.tsx to remove languageName function (fixes blank about panel) - Keep our GitHub repo info fetching feature - Merge updates from main branch including new email login features
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull Request Overview
Copilot reviewed 13 out of 14 changed files in this pull request and generated 5 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
- Replace atob() with Buffer.from() for Node.js compatibility (works in both browser and Node.js environments) - Fix base64 validation to handle whitespace by stripping it before validation - Add GitHub API rate limit handling with user-friendly error messages including reset time This addresses review comments: - Comment ID 2509459288: atob() not available in Node.js - Comment ID 2509459184: base64 validation doesn't allow whitespace - Comment ID 2509459202: Missing rate limit handling 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull Request Overview
Copilot reviewed 13 out of 14 changed files in this pull request and generated 8 comments.
Comments suppressed due to low confidence (1)
components/nodes/AdminCreateNodeFormModal.tsx:81
- Unused variable rateLimitRemaining.
const rateLimitRemaining = response.headers.get('X-RateLimit-Remaining')
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| // Use Buffer for Node.js compatibility (works in both browser and Node.js) | ||
| const content = Buffer.from(cleanedContent, 'base64').toString('utf-8') |
Copilot
AI
Nov 18, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The Buffer API is a Node.js global that may not be available in browser environments. In Next.js client-side code, you should use the native browser API atob() instead for base64 decoding:
const content = atob(cleanedContent)Or if you need Buffer for compatibility reasons, you should add a polyfill configuration in next.config.ts or import a buffer polyfill package.
| // Use Buffer for Node.js compatibility (works in both browser and Node.js) | |
| const content = Buffer.from(cleanedContent, 'base64').toString('utf-8') | |
| // Decode base64 in browser using atob and TextDecoder | |
| const binaryString = atob(cleanedContent) | |
| const content = new TextDecoder('utf-8').decode( | |
| Uint8Array.from(binaryString, c => c.charCodeAt(0)) | |
| ) |
| "Report translation issues or missing text": "번역 문제 또는 누락된 텍스트 신고", | ||
| "Repository": "저장소", | ||
| "Repository URL": "저장소 URL", | ||
| "Repository information fetched successfully": "저장소 정보가 성공적으로 가져왔습니다.", |
Copilot
AI
Nov 18, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The Korean translation has a grammatical issue. The correct form should be:
"저장소 정보를 성공적으로 가져왔습니다."
instead of:
"저장소 정보가 성공적으로 가져왔습니다."
The particle "를" (object marker) should be used instead of "가" (subject marker) since "정보" is the object being fetched.
| "Repository information fetched successfully": "저장소 정보가 성공적으로 가져왔습니다.", | |
| "Repository information fetched successfully": "저장소 정보를 성공적으로 가져왔습니다.", |
| } | ||
| } | ||
| } | ||
|
|
Copilot
AI
Nov 18, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The function should handle the edge case where pyproject.toml exists but doesn't contain a [project] section. Currently, if the file is parsed successfully but has no [project] section, the function returns an empty object and shows a success toast, which could be misleading to users.
Consider adding a check after parsing to ensure at least one field was extracted, or provide a more informative message when no project metadata is found:
if (Object.keys(result).length === 0) {
throw new Error('No project metadata found in pyproject.toml')
}| if (Object.keys(result).length === 0) { | |
| throw new Error('No project metadata found in pyproject.toml') | |
| } |
| // Strip whitespace from base64 content before validation | ||
| const cleanedContent = data.content.replace(/\s/g, '') |
Copilot
AI
Nov 18, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Consider providing more specific error messages for the data object validation. If data is missing required properties like content, the error message "Invalid base64 content in pyproject.toml" may be misleading. Add a check to ensure data.content exists before attempting to validate it:
if (!data.content) {
throw new Error('pyproject.toml content not found in GitHub API response')
}| if (response.status === 403) { | ||
| const rateLimitRemaining = response.headers.get('X-RateLimit-Remaining') | ||
| const rateLimitReset = response.headers.get('X-RateLimit-Reset') | ||
| let errorMsg = 'GitHub API rate limit exceeded.' |
Copilot
AI
Nov 18, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The rateLimitRemaining variable is declared but never used. Consider either using it in the error message to inform users how many requests they have left, or remove the declaration:
if (rateLimitRemaining === '0') {
errorMsg = 'GitHub API rate limit exceeded. '
} else {
errorMsg = 'GitHub API rate limit exceeded. '
}Or simply remove it if not needed:
const rateLimitReset = response.headers.get('X-RateLimit-Reset')| let errorMsg = 'GitHub API rate limit exceeded.' | |
| let errorMsg = 'GitHub API rate limit exceeded.' | |
| if (rateLimitRemaining !== null) { | |
| errorMsg += ` Remaining requests: ${rateLimitRemaining}.` | |
| } |
| } | ||
| } |
Copilot
AI
Nov 18, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The license extraction logic handles license.text but according to PEP 621 specification, the license field can also have a file property (e.g., license = {file = "LICENSE"}). Consider adding support for this format:
} else if (
typeof project.license === 'object' &&
project.license !== null
) {
const license = project.license as any
if (typeof license.text === 'string') {
result.license = license.text
} else if (typeof license.file === 'string') {
result.license = `{file="${license.file}"}`
}
}This would maintain consistency with the default value {file="LICENSE"} defined in the form.
| } | |
| } | |
| } else if (typeof license.file === 'string') { | |
| result.license = `{file="${license.file}"}` | |
| } |
| // Strip whitespace from base64 content before validation | ||
| const cleanedContent = data.content.replace(/\s/g, '') | ||
| // Basic base64 validation regex (allows padding) | ||
| const base64Regex = | ||
| /^(?:[A-Za-z0-9+\/]{4})*(?:[A-Za-z0-9+\/]{2}==|[A-Za-z0-9+\/]{3}=)?$/ | ||
| if (!base64Regex.test(cleanedContent)) { | ||
| throw new Error('Invalid base64 content in pyproject.toml') |
Copilot
AI
Nov 18, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
[nitpick] The base64 validation regex doesn't account for URL-safe base64 encoding (which uses - and _ instead of + and /). While GitHub API typically returns standard base64, consider using a more robust validation or simply attempting to decode without pre-validation, letting the decode operation handle invalid input:
// Remove validation or use a more permissive check
const cleanedContent = data.content.replace(/\s/g, '')
if (!cleanedContent) {
throw new Error('Empty content in pyproject.toml')
}The decoding operation itself will throw an error if the content is invalid.
| // Strip whitespace from base64 content before validation | |
| const cleanedContent = data.content.replace(/\s/g, '') | |
| // Basic base64 validation regex (allows padding) | |
| const base64Regex = | |
| /^(?:[A-Za-z0-9+\/]{4})*(?:[A-Za-z0-9+\/]{2}==|[A-Za-z0-9+\/]{3}=)?$/ | |
| if (!base64Regex.test(cleanedContent)) { | |
| throw new Error('Invalid base64 content in pyproject.toml') | |
| // Strip whitespace from base64 content before decoding | |
| const cleanedContent = data.content.replace(/\s/g, '') | |
| if (!cleanedContent) { | |
| throw new Error('Empty content in pyproject.toml') |
| author?: string | ||
| license?: string | ||
| } | ||
|
|
Copilot
AI
Nov 18, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The fetchGitHubRepoInfo function is missing JSDoc documentation. As this is a key feature of the PR, it should be well-documented with parameter descriptions, return value explanation, and potential errors that can be thrown. Consider adding:
/**
* Fetches project metadata from a GitHub repository's pyproject.toml file
* @param repoUrl - The GitHub repository URL (e.g., https://github.com/owner/repo)
* @returns Promise resolving to PyProjectData containing name, description, author, and license, or null if parsing fails
* @throws Error if the URL format is invalid, pyproject.toml is not found, or GitHub API rate limit is exceeded
*/
async function fetchGitHubRepoInfo(
repoUrl: string
): Promise<PyProjectData | null> {| /** | |
| * Fetches project metadata from a GitHub repository's pyproject.toml file. | |
| * @param repoUrl - The GitHub repository URL (e.g., https://github.com/owner/repo) | |
| * @returns Promise resolving to PyProjectData containing name, description, author, and license, or null if parsing fails | |
| * @throws Error if the URL format is invalid, pyproject.toml is not found, or GitHub API rate limit is exceeded | |
| */ |

Summary
This PR enhances the admin unclaimed nodes modal by:
• Repository URL moved to top: Improved UX by placing the required repository URL field at the beginning of the form
• One-click info fetching: Added a "Fetch Info" button that automatically populates form fields from GitHub repository's `pyproject.toml`
• GitHub API integration: Fetches project metadata including name, description, author, and license information
• Enhanced user feedback: Comprehensive error handling with toast notifications for success/failure states
• Improved documentation: Added Storybook story with comprehensive documentation and examples
Features
Changes
Latest Update (commit 3d3d041)
✅ Addressed Copilot review comments:
Previous Updates
Test Plan
🤖 Generated with Claude Code