Skip to content

Conversation

@snomiao
Copy link
Member

@snomiao snomiao commented Aug 15, 2025

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

image

Features

  • Repository URL input with placeholder and validation
  • "Fetch Info" button with loading states and proper disabled state
  • Automatic form population from `pyproject.toml` parsing
  • Error handling for invalid URLs, missing files, and API failures
  • Toast notifications for user feedback
  • Helper text explaining the functionality
  • Storybook story for component documentation

Changes

Latest Update (commit 3d3d041)

Addressed Copilot review comments:

  • Replaced `atob()` with `Buffer.from()` for Node.js compatibility (works in both browser and Node.js)
  • Fixed base64 validation to handle whitespace by stripping before validation
  • Added GitHub API rate limit handling with user-friendly error messages including reset time

Previous Updates

  • Fixed Spanish and French translations (commit fe694aa)
  • Replaced regex TOML parsing with proper `smol-toml` library
  • Added base64 validation before decoding
  • Fixed TypeScript types in Storybook

Test Plan

  • Build passes without TypeScript errors
  • ESLint passes with no new warnings (existing warning unrelated)
  • Prettier formatting applied
  • Storybook builds successfully and story renders correctly
  • All CICD checks passing
  • Manual testing: Enter GitHub repository URL and click "Fetch Info"
  • Manual testing: Verify form fields populate correctly from pyproject.toml
  • Manual testing: Error handling for invalid URLs and missing files
  • Manual testing: Loading states and button disabled behavior

🤖 Generated with Claude Code

@vercel
Copy link

vercel bot commented Aug 15, 2025

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Preview Comments Updated (UTC)
registry-web Ready Ready Preview Comment Nov 18, 2025 6:19am

@github-actions
Copy link

🎨 Chromatic Visual Testing Results

Resource URL
🔍 Build Results Chromatic Build
📚 Storybook View Storybook

Check the visual changes and approve or request changes as needed.

Copy link
Contributor

Copilot AI left a 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.toml content 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.

snomiao and others added 3 commits October 25, 2025 13:37
- 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>
- 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
Copilot AI review requested due to automatic review settings November 10, 2025 09:12
Copy link
Contributor

Copilot AI left a 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>
Copilot AI review requested due to automatic review settings November 18, 2025 06:17
Copilot finished reviewing on behalf of snomiao November 18, 2025 06:21
Copy link
Contributor

Copilot AI left a 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.

Comment on lines +108 to +109
// Use Buffer for Node.js compatibility (works in both browser and Node.js)
const content = Buffer.from(cleanedContent, 'base64').toString('utf-8')
Copy link

Copilot AI Nov 18, 2025

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.

Suggested change
// 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))
)

Copilot uses AI. Check for mistakes.
"Report translation issues or missing text": "번역 문제 또는 누락된 텍스트 신고",
"Repository": "저장소",
"Repository URL": "저장소 URL",
"Repository information fetched successfully": "저장소 정보가 성공적으로 가져왔습니다.",
Copy link

Copilot AI Nov 18, 2025

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.

Suggested change
"Repository information fetched successfully": "저장소 정보가 성공적으로 가져왔습니다.",
"Repository information fetched successfully": "저장소 정보를 성공적으로 가져왔습니다.",

Copilot uses AI. Check for mistakes.
}
}
}

Copy link

Copilot AI Nov 18, 2025

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')
}
Suggested change
if (Object.keys(result).length === 0) {
throw new Error('No project metadata found in pyproject.toml')
}

Copilot uses AI. Check for mistakes.
Comment on lines +100 to +101
// Strip whitespace from base64 content before validation
const cleanedContent = data.content.replace(/\s/g, '')
Copy link

Copilot AI Nov 18, 2025

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')
}

Copilot uses AI. Check for mistakes.
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.'
Copy link

Copilot AI Nov 18, 2025

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')
Suggested change
let errorMsg = 'GitHub API rate limit exceeded.'
let errorMsg = 'GitHub API rate limit exceeded.'
if (rateLimitRemaining !== null) {
errorMsg += ` Remaining requests: ${rateLimitRemaining}.`
}

Copilot uses AI. Check for mistakes.
Comment on lines +153 to +154
}
}
Copy link

Copilot AI Nov 18, 2025

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.

Suggested change
}
}
} else if (typeof license.file === 'string') {
result.license = `{file="${license.file}"}`
}

Copilot uses AI. Check for mistakes.
Comment on lines +100 to +106
// 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')
Copy link

Copilot AI Nov 18, 2025

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.

Suggested change
// 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')

Copilot uses AI. Check for mistakes.
author?: string
license?: string
}

Copy link

Copilot AI Nov 18, 2025

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> {
Suggested change
/**
* 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
*/

Copilot uses AI. Check for mistakes.
@snomiao
Copy link
Member Author

snomiao commented Nov 19, 2025

image

The error message for the unentered field does not match the language used by the web client.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants