diff --git a/src/context/AppContext.jsx b/src/context/AppContext.jsx
index 0d6279c..74a4a52 100644
--- a/src/context/AppContext.jsx
+++ b/src/context/AppContext.jsx
@@ -33,7 +33,8 @@ export function AppProvider({ children }) {
const [loading, setLoading] = useState(false)
const [loadMsg, setLoadMsg] = useState('')
const [govLoading, setGovLoading] = useState(false)
- const [error, setError] = useState('')
+ const [exploreError, setExploreError] = useState('')
+ const [auditError, setAuditError] = useState('')
const [totalRepo, setTotalRepo] = useState(0)
useEffect(() => {
@@ -75,7 +76,7 @@ export function AppProvider({ children }) {
// Multi-org explore — core of Section 3.2.0
const explore = useCallback(async orgNames => {
- setLoading(true); setError(''); setModel(null); setOrgs([]); setIssuesData({})
+ setLoading(true); setExploreError(''); setModel(null); setOrgs([]); setIssuesData({}); setAuditError('')
try {
setLoadMsg('Fetching organization metadata...')
const orgRes = await Promise.allSettled(orgNames.map(n => fetchOrg(n, pat)))
@@ -124,7 +125,7 @@ export function AppProvider({ children }) {
localStorage.setItem('oe_recent', JSON.stringify([...new Set([entry, ...prev])].slice(0, 6)))
return true
} catch (err) {
- setError(err.message === 'RATE_LIMIT'
+ setExploreError(err.message === 'RATE_LIMIT'
? 'GitHub API rate limit reached. Add a PAT in Settings for 5,000 req/hr.'
: err.message)
return false
@@ -137,18 +138,46 @@ export function AppProvider({ children }) {
const runAudit = useCallback(async () => {
if (!model || govLoading) return
setGovLoading(true)
+ setAuditError('')
const map = {}
- const repos = pat? model.totalRepos : model.totalRepos.slice(0, 15)
+ const repos = pat ? model.totalRepos : model.totalRepos.slice(0, 15)
+ let rateLimitHit = false
+ const otherFailures = []
// Batches of 5 using Promise.allSettled
for (let i = 0; i < repos.length; i += 5) {
const batch = repos.slice(i, i + 5)
- await Promise.allSettled(batch.map(async repo => {
- map[`${repo.orgLogin}/${repo.name}`] = await fetchIssues(repo.orgLogin, repo.name, pat)
+ const results = await Promise.allSettled(batch.map(async repo => {
+ const issues = await fetchIssues(repo.orgLogin, repo.name, pat)
+ map[`${repo.orgLogin}/${repo.name}`] = issues
}))
+
+ for (let j = 0; j < results.length; j++) {
+ const result = results[j]
+ if (result.status === 'rejected') {
+ const msg = result.reason?.message
+ if (msg === 'RATE_LIMIT') {
+ rateLimitHit = true
+ } else {
+ // NOT_FOUND, HTTP_4xx/5xx, network failures, etc.
+ otherFailures.push(batch[j] ? `${batch[j].orgLogin}/${batch[j].name}` : 'unknown repo')
+ }
+ }
+ }
+
+ if (rateLimitHit) {
+ break // no point continuing — remaining fetches will also fail
+ }
}
+
setIssuesData(map)
setGovLoading(false)
+
+ if (rateLimitHit) {
+ setAuditError('GitHub API rate limit reached during audit. Results shown may be incomplete. Add a PAT in Settings for 5,000 req/hr.')
+ } else if (otherFailures.length) {
+ setAuditError(`Audit completed with errors. Failed to fetch issues for: ${otherFailures.join(', ')}. Results may be incomplete.`)
+ }
}, [model, pat, govLoading])
const STALE_DAYS = 90
@@ -188,8 +217,10 @@ export function AppProvider({ children }) {
return (
Type an org name and press Enter or comma to add. Add multiple orgs to analyze as a unified portfolio.
- {error &&{error}
} + {exploreError &&{exploreError}
}