Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
47 changes: 39 additions & 8 deletions src/context/AppContext.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -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(() => {
Expand Down Expand Up @@ -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)))
Expand Down Expand Up @@ -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
Expand All @@ -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
}
}
Comment thread
coderabbitai[bot] marked this conversation as resolved.

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])
Comment thread
coderabbitai[bot] marked this conversation as resolved.

const STALE_DAYS = 90
Expand Down Expand Up @@ -188,8 +217,10 @@ export function AppProvider({ children }) {
return (
<Ctx.Provider value={{
pat, savePat, orgs, model, issuesData,
rateLimit, loading, loadMsg, govLoading, error, totalRepo,
explore, runAudit, setError, refreshRateLimit, staleRepoStats,
rateLimit, loading, loadMsg, govLoading, totalRepo,
exploreError, setExploreError,
auditError, setAuditError,
explore, runAudit, refreshRateLimit, staleRepoStats,
}}>
{children}
</Ctx.Provider>
Expand Down
25 changes: 24 additions & 1 deletion src/pages/GovernancePage.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ const getStatus = ratio => {
}

export default function GovernancePage() {
const { model, issuesData, runAudit, govLoading, staleRepoStats } = useApp()
const { model, issuesData, runAudit, govLoading, staleRepoStats, auditError, setAuditError } = useApp()
const [tab, setTab] = useState('dead')

const ITEMS_PER_PAGE = 10
Expand Down Expand Up @@ -152,6 +152,29 @@ export default function GovernancePage() {
}
/>

{/* Audit error banner */}
{auditError && (
<div
role="alert"
style={{
display: 'flex', alignItems: 'center', justifyContent: 'space-between',
background: 'rgba(239,68,68,.08)', border: '1px solid rgba(239,68,68,.3)',
borderRadius: 8, padding: '10px 16px', marginBottom: 20,
fontSize: 13, color: 'var(--red)',
}}
>
<span>{auditError}</span>
<button
type="button"
onClick={() => setAuditError('')}
style={{ background: 'none', border: 'none', cursor: 'pointer', color: 'var(--red)', fontSize: 16, lineHeight: 1, padding: '0 4px' }}
aria-label="Dismiss error"
>
×
</button>
Comment thread
coderabbitai[bot] marked this conversation as resolved.
</div>
)}

{/* Summary stat cards */}
<div style={{ display: 'grid', gridTemplateColumns: 'repeat(4,1fr)', gap: 12, marginBottom: 24 }}>
<StatBox label="Dead Issues" value={counts.dead} sub="OPEN 90+ DAYS" color="var(--red)" />
Expand Down
4 changes: 2 additions & 2 deletions src/pages/HomePage.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import { C, Spinner } from '../components/UI'
const QUICK = ['AOSSIE-Org', 'DjedAlliance', 'StabilityNexus']

export default function HomePage() {
const { explore, loading, loadMsg, error } = useApp()
const { explore, loading, loadMsg, exploreError } = useApp()
const navigate = useNavigate()
const [input, setInput] = useState('')
const [chips, setChips] = useState([])
Expand Down Expand Up @@ -89,7 +89,7 @@ export default function HomePage() {
<p style={{ fontSize: 11, color: 'var(--text3)', marginTop: 6, paddingLeft: 4 }}>
Type an org name and press Enter or comma to add. Add multiple orgs to analyze as a unified portfolio.
</p>
{error && <p style={{ color: 'var(--red)', fontSize: 12, marginTop: 8 }}>{error}</p>}
{exploreError && <p style={{ color: 'var(--red)', fontSize: 12, marginTop: 8 }}>{exploreError}</p>}
</div>

{/* Loading */}
Expand Down
Loading