-
Notifications
You must be signed in to change notification settings - Fork 80
feat(WIP): New IssueExplor PR #412
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
Closed
Closed
Changes from 11 commits
Commits
Show all changes
38 commits
Select commit
Hold shift + click to select a range
45563aa
feat: New IssueExplor PR
aceppaluni b6e30a6
fix: Fix Deployment
aceppaluni 143279e
fix: Fix Codacy
aceppaluni 8532a72
fix: Fix Format And Codacy
aceppaluni 22da94a
fix: Fix Errors
aceppaluni 5de0081
fix: Fix Codacy Errors
aceppaluni 4e702a5
fix: Fix Codacy With Rule
aceppaluni 2346729
fix: Fix Final Codacy Errors
aceppaluni cb8c6f0
fix: Fix Updated Errs
aceppaluni b2ccc0c
fix: Make New Changes
aceppaluni 82287e4
fix: Make Codacy Changes
aceppaluni ab654d6
fix: Make New Codacy Changes
aceppaluni ddf57e9
fix: Make Additional Codacy Changes
aceppaluni f354bb9
fix: Fixing Codacy
aceppaluni 3ac912b
fix: Fixing More Codacy
aceppaluni fcf53a9
fix: Implement Suggestions
aceppaluni 33f3807
fix: Fix Some Codacy Issues
aceppaluni e91e4d9
fix: Fix Some More Codacy Issues
aceppaluni 5d3f6ba
fix: Codacy
aceppaluni ad3dea2
fix: Fix Codacy Phase 1
aceppaluni 1b0ebac
fix: Fix Codacy Phase 2
aceppaluni 7e6edc7
fix: Fix Codacy Phase 3
aceppaluni 5ffd5a1
fix: Fix Codacy Phase 4
aceppaluni 4448acb
fix: Fix Codacy Phase 5
aceppaluni 787d3a7
fix: Fix Codacy Phase 6
aceppaluni dbb9ae6
fix: Fix Codacy Phase 7
aceppaluni bde4b08
fix: Fix Codacy Phase 8
aceppaluni cf19ca1
fix: Fix Codacy Phase 9
aceppaluni 07345b6
fix: Fix Codacy Phase 10
aceppaluni acfaa67
fix: Fix Codacy Phase 11
aceppaluni d1767ad
fix: Fix Codacy Phase 12
aceppaluni 597ab48
fix: Fix Codacy Phase 13
aceppaluni ba53e00
fix: Fix Codacy Phase 14
aceppaluni c647ee5
fix: Fix Codacy Phase 15
aceppaluni d0ce6b8
fix: Fix Codacy Phase 16
aceppaluni a291588
fix: Fix Codacy Phase 17
aceppaluni 725185d
fix: Fix Codacy Phase 18
aceppaluni 803cc10
Merge branch 'main' into IssueTab
aceppaluni File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,6 +1,9 @@ | ||
| [build] | ||
| command = "pnpm build" | ||
| publish = "out" | ||
| publish = ".next" | ||
|
|
||
| [build.environment] | ||
| NODE_VERSION = "20" | ||
| NODE_VERSION = "20" | ||
|
|
||
| [[plugins]] | ||
| package = "@netlify/plugin-nextjs" |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,27 @@ | ||
| import { searchIssues as searchIssues } from "src/lib/github/issues"; | ||
|
|
||
| function getStatus(error: unknown): number { | ||
| if (typeof error === "object" && error !== null && "status" in error) { | ||
| const status = (error as { status?: unknown }).status; | ||
| if (typeof status === "number") return status; | ||
| } | ||
|
|
||
| return 502; | ||
| } | ||
|
|
||
| export async function GET(req: Request) { | ||
| const { searchParams } = new URL(req.url); | ||
| const q = searchParams.get("q") ?? ""; | ||
|
|
||
| try { | ||
| const data = await searchIssues(q); | ||
|
Check warning on line 17 in src/app/api/issues/route.ts
|
||
| return Response.json(data); | ||
| } catch (error) { | ||
| const status = getStatus(error); | ||
|
|
||
| const message = | ||
| error instanceof Error ? error.message : "Failed to fetch issues"; | ||
|
|
||
| return Response.json({ items: [], error: message }, { status }); | ||
| } | ||
| } | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,222 @@ | ||
| "use client"; | ||
|
|
||
| import RichText from "@/components/RichText"; | ||
| import Container from "@/components/Container"; | ||
| import { useEffect, useState } from "react"; | ||
|
|
||
| /* ----------------------------- | ||
| Types | ||
| ------------------------------ */ | ||
| interface GitHubIssue { | ||
| id: number; | ||
| title: string; | ||
| html_url: string; | ||
| repository_url: string; | ||
| } | ||
|
|
||
| interface GitHubSearchResponse { | ||
| items: GitHubIssue[]; | ||
| error?: string; | ||
| } | ||
|
|
||
| /* ----------------------------- | ||
| Debounce hook | ||
| ------------------------------ */ | ||
| function useDebouncedValue<T>(value: T, delay = 400) { | ||
| const [debounced, setDebounced] = useState(value); | ||
|
|
||
| useEffect(() => { | ||
| const timer = setTimeout(() => { | ||
| setDebounced(value); | ||
| }, delay); | ||
|
|
||
| return () => { | ||
| clearTimeout(timer); | ||
| }; | ||
| }, [value, delay]); | ||
|
|
||
| return debounced; | ||
| } | ||
|
|
||
| /* ----------------------------- | ||
| Maps | ||
| ------------------------------ */ | ||
| const sdkMap: Record<string, string> = { | ||
| python: "repo:hiero-ledger/hiero-sdk-python", | ||
| javascript: "repo:hiero-ledger/hiero-sdk-js", | ||
| cpp: "repo:hiero-ledger/hiero-sdk-cpp", | ||
| java: "repo:hiero-ledger/hiero-sdk-java", | ||
| go: "repo:hiero-ledger/hiero-sdk-go", | ||
| rust: "repo:hiero-ledger/hiero-sdk-rust", | ||
| block_node: "repo:hiero-ledger/hiero-block-node", | ||
| mirror_node: "repo:hiero-ledger/hiero-mirror-node", | ||
| consensus_node: "repo:hiero-ledger/hiero-consensus-node", | ||
| hiero_docs: "repo:hiero-ledger/hiero-docs", | ||
| }; | ||
|
|
||
| const difficultyMap: Record<string, string[]> = { | ||
| "good first issue": [ | ||
| "good first issue", | ||
| "good-first-issue", | ||
| "starter", | ||
| "easy", | ||
| ], | ||
| beginner: ["beginner", "easy", "starter"], | ||
| intermediate: ["intermediate"], | ||
| advanced: ["advanced"], | ||
| }; | ||
|
|
||
| /* ----------------------------- | ||
| Cache | ||
| ------------------------------ */ | ||
| const cache = new Map<string, GitHubSearchResponse>(); | ||
|
|
||
| /* ----------------------------- | ||
| Helpers | ||
| ------------------------------ */ | ||
| function matchesDifficulty(issue: GitHubIssue, difficulty: string) { | ||
| if (!difficulty) return true; | ||
|
|
||
| const text = issue.title.toLowerCase(); | ||
| const keywords = difficultyMap[difficulty]; | ||
|
|
||
| if (!keywords) return true; | ||
|
|
||
| return keywords.some(k => text.includes(k)); | ||
| } | ||
|
|
||
| /* ----------------------------- | ||
| Component | ||
| ------------------------------ */ | ||
| export default function GoodFirstIssues() { | ||
| const [issues, setIssues] = useState<GitHubIssue[]>([]); | ||
| const [loading, setLoading] = useState(false); | ||
| const [error, setError] = useState<string | null>(null); | ||
|
|
||
| const [difficulty, setDifficulty] = useState(""); | ||
| const [sdk, setSdk] = useState(""); | ||
|
|
||
| const debouncedDifficulty = useDebouncedValue(difficulty); | ||
| const debouncedSdk = useDebouncedValue(sdk); | ||
|
|
||
| const getIssues = async (query: string, signal?: AbortSignal) => { | ||
| if (cache.has(query)) { | ||
| return cache.get(query)!; | ||
| } | ||
|
|
||
| const res = await fetch(`/api/issues?q=${encodeURIComponent(query)}`, { | ||
| signal, | ||
| }); | ||
|
|
||
| const data = (await res.json()) as GitHubSearchResponse; | ||
|
|
||
| if (!res.ok) { | ||
| throw new Error(data.error ?? "Failed to fetch issues"); | ||
| } | ||
|
|
||
| cache.set(query, data); | ||
| return data; | ||
| }; | ||
|
|
||
| useEffect(() => { | ||
| const controller = new AbortController(); | ||
|
|
||
| const fetchIssues = async () => { | ||
| setLoading(true); | ||
| setError(null); | ||
|
|
||
| try { | ||
| const base = "is:issue state:open"; | ||
|
|
||
| const repos = | ||
| debouncedSdk && debouncedSdk in sdkMap | ||
| ? [sdkMap[debouncedSdk]] | ||
| : Object.values(sdkMap); | ||
|
|
||
| const results = await Promise.all( | ||
| repos.map(repo => { | ||
| const query = `${base} ${repo}`; | ||
| return getIssues(query, controller.signal); | ||
| }), | ||
| ); | ||
|
|
||
| const merged = results.flatMap(r => r.items); | ||
|
|
||
| const unique = Array.from(new Map(merged.map(i => [i.id, i])).values()); | ||
|
|
||
| const filtered = debouncedDifficulty | ||
| ? unique.filter(i => matchesDifficulty(i, debouncedDifficulty)) | ||
| : unique; | ||
|
|
||
| setIssues(filtered); | ||
| } catch (err) { | ||
| if (err instanceof DOMException && err.name === "AbortError") return; | ||
|
|
||
| setError(err instanceof Error ? err.message : "Unknown error occurred"); | ||
| setIssues([]); | ||
| } finally { | ||
| setLoading(false); | ||
| } | ||
| }; | ||
|
|
||
| void fetchIssues(); | ||
|
|
||
| return () => { | ||
| controller.abort(); | ||
| }; | ||
| }, [debouncedDifficulty, debouncedSdk]); | ||
|
|
||
| return ( | ||
| <Container> | ||
| {/* Filters */} | ||
| <div className="flex gap-4 mb-6"> | ||
| <select | ||
| value={difficulty} | ||
| onChange={e => setDifficulty(e.target.value)} | ||
| className="p-2 rounded border"> | ||
| <option value="">All Difficulties</option> | ||
| <option value="good first issue">Good First Issue</option> | ||
| <option value="beginner">Beginner</option> | ||
| <option value="intermediate">Intermediate</option> | ||
| <option value="advanced">Advanced</option> | ||
| </select> | ||
|
|
||
| <select | ||
| value={sdk} | ||
| onChange={e => setSdk(e.target.value)} | ||
| className="p-2 rounded border"> | ||
| <option value="">All Repos</option> | ||
| <option value="python">Python</option> | ||
| <option value="javascript">JavaScript</option> | ||
| <option value="cpp">C++</option> | ||
| <option value="java">Java</option> | ||
| <option value="go">Go</option> | ||
| <option value="rust">Rust</option> | ||
| <option value="block_node">Block Node</option> | ||
| <option value="mirror_node">Mirror Node</option> | ||
| <option value="consensus_node">Consensus Node</option> | ||
| <option value="hiero_docs">Hiero Docs</option> | ||
| </select> | ||
| </div> | ||
|
|
||
| {/* Issues */} | ||
| {loading && <p>Loading issues...</p>} | ||
| {error && <p className="text-red-500">{error}</p>} | ||
|
|
||
| <div className="grid grid-cols-1 sm:grid-cols-2 md:grid-cols-3 lg:grid-cols-4 gap-6"> | ||
| {issues.map(issue => ( | ||
| <div | ||
| key={issue.id} | ||
| className="bg-gradient-to-br from-white-dark via-white to-white p-4 rounded-xl shadow-md"> | ||
| <a href={issue.html_url} target="_blank" rel="noopener noreferrer"> | ||
| <RichText markdown={issue.title} className="line-clamp-2" /> | ||
| </a> | ||
| <p className="text-sm opacity-70 mt-2"> | ||
| {issue.repository_url.split("/").pop()} | ||
| </p> | ||
| </div> | ||
| ))} | ||
| </div> | ||
| </Container> | ||
| ); | ||
| } | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,48 @@ | ||
| export interface GitHubIssue { | ||
| id: number; | ||
| title: string; | ||
| html_url: string; | ||
| repository_url: string; | ||
| } | ||
|
|
||
| export interface GitHubSearchResponse { | ||
| items: GitHubIssue[]; | ||
| total_count: number; | ||
| } | ||
|
|
||
| export async function searchIssues( | ||
| query: string, | ||
| ): Promise<GitHubSearchResponse> { | ||
| const headers: Record<string, string> = { | ||
| Accept: "application/vnd.github+json", | ||
| "User-Agent": "hiero-website", | ||
| }; | ||
|
|
||
| if (process.env.GITHUB_TOKEN) { | ||
| headers.Authorization = `Bearer ${process.env.GITHUB_TOKEN}`; | ||
| } | ||
|
|
||
| const res = await fetch( | ||
| `https://api.github.com/search/issues?q=${encodeURIComponent(query)}`, | ||
| { headers }, | ||
| ); | ||
|
|
||
| if (!res.ok) { | ||
| const body = await res.json().catch(() => ({})); | ||
| const message = | ||
| typeof body?.message === "string" | ||
| ? body.message | ||
| : "GitHub request failed"; | ||
| throw Object.assign(new Error(message), { status: res.status }); | ||
| } | ||
|
|
||
| return (await res.json()) as GitHubSearchResponse; | ||
| } | ||
|
|
||
| export async function GET(req: Request) { | ||
| const { searchParams } = new URL(req.url); | ||
| const q = searchParams.get("q") || ""; | ||
|
|
||
| const data = await searchIssues(q); | ||
| return Response.json(data); | ||
| } |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.