From f0118675a347fcb1dcbca19a4308b09b4dba8aa8 Mon Sep 17 00:00:00 2001 From: Ridima28 Date: Tue, 23 Jun 2026 11:34:34 +0530 Subject: [PATCH 1/4] feat: added the logic of searching github orgs in github.js. added a state in home to control the suggestion display --- src/pages/HomePage.jsx | 248 +++++++++++++++++++++-------------------- src/services/github.js | 13 +++ 2 files changed, 138 insertions(+), 123 deletions(-) diff --git a/src/pages/HomePage.jsx b/src/pages/HomePage.jsx index cffcbd3..935688f 100644 --- a/src/pages/HomePage.jsx +++ b/src/pages/HomePage.jsx @@ -1,142 +1,144 @@ -import React, { useState } from 'react' -import { useNavigate } from 'react-router-dom' -import { FiSearch, FiX } from 'react-icons/fi' -import { useApp } from '../context/AppContext' -import { C, Spinner } from '../components/UI' + import React, { useState,useEffect } from 'react' + import { useNavigate } from 'react-router-dom' + import { FiSearch, FiX } from 'react-icons/fi' + import { useApp } from '../context/AppContext' + import { C, Spinner } from '../components/UI' -const QUICK = ['AOSSIE-Org', 'DjedAlliance', 'StabilityNexus'] + const QUICK = ['AOSSIE-Org', 'DjedAlliance', 'StabilityNexus'] -export default function HomePage() { - const { explore, loading, loadMsg, error } = useApp() - const navigate = useNavigate() - const [input, setInput] = useState('') - const [chips, setChips] = useState([]) + export default function HomePage() { + const { explore, loading, loadMsg, error } = useApp() + const navigate = useNavigate() + const [input, setInput] = useState('') + const [chips, setChips] = useState([]) + const[suggestions, setSuggestion] = useState([]) + const[showSuggestion, setShowSuggestions] = useState(false) - const recent = JSON.parse(localStorage.getItem('oe_recent') || '[]') + const recent = JSON.parse(localStorage.getItem('oe_recent') || '[]') - const addChip = raw => { - const parts = raw.split(/[,+\s]+/).map(s => s.trim()).filter(Boolean) - setChips(prev => [...new Set([...prev, ...parts])]) - setInput('') - } + const addChip = raw => { + const parts = raw.split(/[,+\s]+/).map(s => s.trim()).filter(Boolean) + setChips(prev => [...new Set([...prev, ...parts])]) + setInput('') + } - const removeChip = c => setChips(prev => prev.filter(x => x !== c)) + const removeChip = c => setChips(prev => prev.filter(x => x !== c)) - const handleKey = e => { - if ((e.key === 'Enter' || e.key === ',') && input.trim()) { - e.preventDefault() - addChip(input) - } - if (e.key === 'Backspace' && !input && chips.length) { - setChips(prev => prev.slice(0, -1)) + const handleKey = e => { + if ((e.key === 'Enter' || e.key === ',') && input.trim()) { + e.preventDefault() + addChip(input) + } + if (e.key === 'Backspace' && !input && chips.length) { + setChips(prev => prev.slice(0, -1)) + } } - } - const go = async (targets) => { - const orgs = targets || (chips.length ? chips : input.trim() ? [input.trim()] : []) - if (!orgs.length) return - const success = await explore(orgs) - if(success) navigate('/overview') - } - - return ( -
- {/* Hero */} -
-

- Architect Your{' '} - Insights -

-

- Unified analytics across one or many GitHub organizations. Multi-org portfolio analysis, contributor network graphs, time-series trends, and governance audits — entirely in the browser. -

-
+ const go = async (targets) => { + const orgs = targets || (chips.length ? chips : input.trim() ? [input.trim()] : []) + if (!orgs.length) return + const success = await explore(orgs) + if(success) navigate('/overview') + } - {/* Search */} -
-
- - {chips.map(c => ( - - {c} - removeChip(c)} /> - - ))} - setInput(e.target.value)} - onKeyDown={handleKey} - onBlur={() => input.trim() && addChip(input)} - placeholder={chips.length ? 'Add another org...' : 'AOSSIE-Org, StabilityNexus, DjedAlliance...'} - style={{ flex: 1, minWidth: 160, background: 'none', color: 'var(--text)', fontSize: 14, padding: '4px 8px', border: 'none', outline: 'none' }} - /> - + return ( +
+ {/* Hero */} +
+

+ Architect Your{' '} + Insights +

+

+ Unified analytics across one or many GitHub organizations. Multi-org portfolio analysis, contributor network graphs, time-series trends, and governance audits — entirely in the browser. +

-

- Type an org name and press Enter or comma to add. Add multiple orgs to analyze as a unified portfolio. -

- {error &&

{error}

} -
- {/* Loading */} - {loading && ( -
- -

{loadMsg}

-
- )} - - {/* Recent */} - {recent.length > 0 && !loading && ( -
- Recent searches -
- {recent.map(r => ( - + {/* Search */} +
+
+ + {chips.map(c => ( + + {c} + removeChip(c)} /> + ))} + setInput(e.target.value)} + onKeyDown={handleKey} + onBlur={() => input.trim() && addChip(input)} + placeholder={chips.length ? 'Add another org...' : 'AOSSIE-Org, StabilityNexus, DjedAlliance...'} + style={{ flex: 1, minWidth: 160, background: 'none', color: 'var(--text)', fontSize: 14, padding: '4px 8px', border: 'none', outline: 'none' }} + /> +
+

+ Type an org name and press Enter or comma to add. Add multiple orgs to analyze as a unified portfolio. +

+ {error &&

{error}

}
- )} - {/* Quick explore */} - {!loading && ( -
- Quick explore -
- {QUICK.map(q => ( - - ))} + {/* Loading */} + {loading && ( +
+ +

{loadMsg}

-
- )} + )} + + {/* Recent */} + {recent.length > 0 && !loading && ( +
+ Recent searches +
+ {recent.map(r => ( + + ))} +
+
+ )} - {/* Stats bar */} -
- {[['5,000', 'req/hr with PAT', 'var(--green)'], ['1HR', 'intelligent cache', 'var(--green)'], ['ZERO', 'backend latency', 'var(--accent)']].map(([v, l, color]) => ( -
-
{v}
-
{l}
+ {/* Quick explore */} + {!loading && ( +
+ Quick explore +
+ {QUICK.map(q => ( + + ))} +
- ))} + )} + + {/* Stats bar */} +
+ {[['5,000', 'req/hr with PAT', 'var(--green)'], ['1HR', 'intelligent cache', 'var(--green)'], ['ZERO', 'backend latency', 'var(--accent)']].map(([v, l, color]) => ( +
+
{v}
+
{l}
+
+ ))} +
-
- ) -} + ) + } diff --git a/src/services/github.js b/src/services/github.js index 8b10597..f2c1dc2 100644 --- a/src/services/github.js +++ b/src/services/github.js @@ -85,6 +85,19 @@ export async function fetchRepos(org, pat) { return all } +const searchOrgs = async (query) => { + if (!query) return [] + + const res = await fetch( + `https://api.github.com/search/users?q=${encodeURIComponent(query)}+type:org` + ) + if (!res.ok) return [] + + const data = await res.json() + return data.items || [] +} + + export async function fetchContributors(org, repo, pat) { try { return await fetchWithCache( From cf8d09a021f77f7ada6e01fb6ee733fbffb05c91 Mon Sep 17 00:00:00 2001 From: Ridima28 Date: Tue, 23 Jun 2026 11:48:04 +0530 Subject: [PATCH 2/4] feat: added a dropdown after input box, which shows all the suggested organization --- src/pages/HomePage.jsx | 63 +++++++++++++++++++++++++++++++++++++++++- src/services/github.js | 2 +- 2 files changed, 63 insertions(+), 2 deletions(-) diff --git a/src/pages/HomePage.jsx b/src/pages/HomePage.jsx index 935688f..dfdd559 100644 --- a/src/pages/HomePage.jsx +++ b/src/pages/HomePage.jsx @@ -3,6 +3,7 @@ import { FiSearch, FiX } from 'react-icons/fi' import { useApp } from '../context/AppContext' import { C, Spinner } from '../components/UI' + import { searchOrgs } from '../services/github' const QUICK = ['AOSSIE-Org', 'DjedAlliance', 'StabilityNexus'] @@ -14,6 +15,26 @@ const[suggestions, setSuggestion] = useState([]) const[showSuggestion, setShowSuggestions] = useState(false) + useEffect(() => { + if (!input.trim()) { + setSuggestion([]) + return + } + const t = setTimeout(async () => { + const result = await searchOrgs(input.trim()) + setSuggestion(result.slice(0,6)) + setShowSuggestions(true) + }, 300) + return () => clearTimeout(t) + }, [input]) + + const selectSuggestion = (login) => { + setChips(prev => [...new Set([...prev,login])]) + setInput('') + setSuggestion([]) + setShowSuggestions(false) + } + const recent = JSON.parse(localStorage.getItem('oe_recent') || '[]') const addChip = raw => { @@ -59,7 +80,7 @@
{/* Search */} -
+
+ @@ -92,6 +114,45 @@ Type an org name and press Enter or comma to add. Add multiple orgs to analyze as a unified portfolio.

{error &&

{error}

} + {showSuggestion && suggestions.length > 0 && ( +
+
+ {suggestions.map(org => ( +
selectSuggestion(org.login)} + style={{ + padding: '10px 12px', + cursor: 'pointer', + fontSize: 13, + display: 'flex', + justifyContent: 'space-between' + }} + > + {org.login} + + org + +
+ ))} +
+
+ )} +
{/* Loading */} diff --git a/src/services/github.js b/src/services/github.js index f2c1dc2..1d11002 100644 --- a/src/services/github.js +++ b/src/services/github.js @@ -85,7 +85,7 @@ export async function fetchRepos(org, pat) { return all } -const searchOrgs = async (query) => { +export const searchOrgs = async (query) => { if (!query) return [] const res = await fetch( From 4784ff0e3b3b3087bb187bca535d45f62923bad7 Mon Sep 17 00:00:00 2001 From: Ridima28 Date: Tue, 23 Jun 2026 11:56:39 +0530 Subject: [PATCH 3/4] fix: added the update in readme file --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 7cb9cea..8c27c4e 100644 --- a/README.md +++ b/README.md @@ -49,6 +49,7 @@ - **Fully Browser-Based** — Runs entirely in the browser using GitHub APIs with no backend server required. - **Organization Overview Dashboard** — Explore repositories, contributors, activity trends, tech stack distribution, and organization growth insights. +- **Smart Organization Search with Autocomplete** — GitHub org suggestions with real-time dropdown search - **Advanced Repository Analytics** — Analyze repository activity, contributor density, issue and PR trends, health metrics, and lifecycle status. From c5e796a5206eb99e7cc324d1989c62cd7bbea90f Mon Sep 17 00:00:00 2001 From: Ridima28 Date: Tue, 23 Jun 2026 12:11:50 +0530 Subject: [PATCH 4/4] fix: fixed the blur race condition --- src/pages/HomePage.jsx | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/pages/HomePage.jsx b/src/pages/HomePage.jsx index dfdd559..208127a 100644 --- a/src/pages/HomePage.jsx +++ b/src/pages/HomePage.jsx @@ -12,17 +12,17 @@ const navigate = useNavigate() const [input, setInput] = useState('') const [chips, setChips] = useState([]) - const[suggestions, setSuggestion] = useState([]) + const[suggestions, setSuggestions] = useState([]) const[showSuggestion, setShowSuggestions] = useState(false) useEffect(() => { if (!input.trim()) { - setSuggestion([]) + setSuggestions([]) return } const t = setTimeout(async () => { const result = await searchOrgs(input.trim()) - setSuggestion(result.slice(0,6)) + setSuggestions(result.slice(0,6)) setShowSuggestions(true) }, 300) return () => clearTimeout(t) @@ -101,12 +101,12 @@ value={input} onChange={e => setInput(e.target.value)} onKeyDown={handleKey} - onBlur={() => input.trim() && addChip(input)} + onBlur={() => !showSuggestions && input.trim() && addChip(input)} + aria-label="Search GitHub organizations" placeholder={chips.length ? 'Add another org...' : 'AOSSIE-Org, StabilityNexus, DjedAlliance...'} style={{ flex: 1, minWidth: 160, background: 'none', color: 'var(--text)', fontSize: 14, padding: '4px 8px', border: 'none', outline: 'none' }} /> - -