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
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 5 additions & 1 deletion src/App.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,16 @@
import './App.css'
import RateLimitIndicator from "./RateLimitIndicator";

function App() {

return (
<>
<h1>Hello, OrgExplorer!</h1>

<RateLimitIndicator />

</>
)
}

export default App
export default App
53 changes: 53 additions & 0 deletions src/RateLimitIndicator.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import { useEffect, useState } from "react";

interface RateLimitData {
limit: number;
remaining: number;
reset: number;
}

export default function RateLimitIndicator() {
const [rateLimit, setRateLimit] = useState<RateLimitData | null>(null);

useEffect(() => {
async function fetchRateLimit() {
try {
const response = await fetch("https://api.github.com/rate_limit");
const data = await response.json();

setRateLimit({
limit: data.rate.limit,
remaining: data.rate.remaining,
reset: data.rate.reset,
});
} catch (error) {
console.error("Error fetching rate limit:", error);
}
}

fetchRateLimit();
}, []);
Comment on lines +12 to +29
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Refresh the quota after GitHub API activity.

This effect runs only once on mount, so remaining and reset become stale as soon as the user makes more GitHub requests. That undercuts the goal of using this widget to monitor quota while exploring orgs. Please either wire it to the same GitHub request layer the app uses or poll/refetch on an interval.

♻️ Minimal polling-based fix
   useEffect(() => {
+    let cancelled = false;
+
     async function fetchRateLimit() {
       try {
         const response = await fetch("https://api.github.com/rate_limit");
         const data = await response.json();
 
-        setRateLimit({
-          limit: data.rate.limit,
-          remaining: data.rate.remaining,
-          reset: data.rate.reset,
-        });
+        if (!cancelled) {
+          setRateLimit({
+            limit: data.rate.limit,
+            remaining: data.rate.remaining,
+            reset: data.rate.reset,
+          });
+        }
       } catch (error) {
         console.error("Error fetching rate limit:", error);
       }
     }
 
     fetchRateLimit();
+    const intervalId = window.setInterval(fetchRateLimit, 30000);
+
+    return () => {
+      cancelled = true;
+      window.clearInterval(intervalId);
+    };
   }, []);
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
useEffect(() => {
async function fetchRateLimit() {
try {
const response = await fetch("https://api.github.com/rate_limit");
const data = await response.json();
setRateLimit({
limit: data.rate.limit,
remaining: data.rate.remaining,
reset: data.rate.reset,
});
} catch (error) {
console.error("Error fetching rate limit:", error);
}
}
fetchRateLimit();
}, []);
useEffect(() => {
let cancelled = false;
async function fetchRateLimit() {
try {
const response = await fetch("https://api.github.com/rate_limit");
const data = await response.json();
if (!cancelled) {
setRateLimit({
limit: data.rate.limit,
remaining: data.rate.remaining,
reset: data.rate.reset,
});
}
} catch (error) {
console.error("Error fetching rate limit:", error);
}
}
fetchRateLimit();
const intervalId = window.setInterval(fetchRateLimit, 30000);
return () => {
cancelled = true;
window.clearInterval(intervalId);
};
}, []);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/RateLimitIndicator.tsx` around lines 12 - 29, The current useEffect
fetchRateLimit runs only on mount so remaining/reset go stale; update
RateLimitIndicator by either subscribing to the app's GitHub request layer
(e.g., hook into the same client/event emitter) or implement polling inside
useEffect: move fetchRateLimit into a named async function (fetchRateLimit) and
call it on an interval via setInterval, storing the timer and clearing it on
cleanup, and continue to call setRateLimit with data.rate.*; ensure useEffect
returns a cleanup that clears the interval to avoid leaks.


if (!rateLimit) return <p>Loading API status...</p>;
Comment on lines +15 to +31
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Don't leave the widget stuck in loading on failed responses.

If GitHub returns 403/5xx or an unexpected payload, the catch path only logs and rateLimit stays null, so the UI keeps showing Loading API status... forever even though loading already finished. Check response.ok and render an explicit unavailable/error state.

🛠️ Suggested error-state handling
 export default function RateLimitIndicator() {
   const [rateLimit, setRateLimit] = useState<RateLimitData | null>(null);
+  const [status, setStatus] = useState<"loading" | "ready" | "error">("loading");
 
   useEffect(() => {
     async function fetchRateLimit() {
       try {
         const response = await fetch("https://api.github.com/rate_limit");
+        if (!response.ok) {
+          throw new Error(`GitHub rate limit request failed: ${response.status}`);
+        }
         const data = await response.json();
 
         setRateLimit({
           limit: data.rate.limit,
           remaining: data.rate.remaining,
           reset: data.rate.reset,
         });
+        setStatus("ready");
       } catch (error) {
         console.error("Error fetching rate limit:", error);
+        setStatus("error");
       }
     }
 
     fetchRateLimit();
   }, []);
 
-  if (!rateLimit) return <p>Loading API status...</p>;
+  if (status === "loading") return <p>Loading API status...</p>;
+  if (status === "error" || !rateLimit) return <p>API status unavailable</p>;
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/RateLimitIndicator.tsx` around lines 15 - 31, The fetchRateLimit function
leaves rateLimit null on HTTP errors/unexpected payloads which makes the UI show
"Loading API status..." forever; update fetchRateLimit to check response.ok
after the fetch, handle non-OK statuses by calling setRateLimit with an explicit
error/unavailable sentinel (e.g., an object like {error: true, message: ...})
and also validate the parsed payload before reading data.rate; ensure the
component reads rateLimit.error (or similar) to render an "API unavailable" or
error state instead of the loading message, and keep console.error for debugging
inside the catch branch.


const resetTime = new Date(rateLimit.reset * 1000).toLocaleTimeString();

return (
<div
style={{
position: "fixed",
bottom: "20px",
right: "20px",
background: "#1f2937",
padding: "12px",
borderRadius: "8px",
color: "white",
fontSize: "14px",
}}
>
<strong>GitHub API Status</strong>
<p>Remaining: {rateLimit.remaining} / {rateLimit.limit}</p>
<p>Reset: {resetTime}</p>
Comment on lines +31 to +50
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Externalize the new UI copy.

Loading API status..., GitHub API Status, Remaining, and Reset are hardcoded here. Please move them into the app's i18n resources before shipping.

As per coding guidelines, "Internationalization: User-visible strings should be externalized to resource files (i18n)".

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/RateLimitIndicator.tsx` around lines 31 - 50, The component
RateLimitIndicator currently hardcodes user-facing strings ("Loading API
status...", "GitHub API Status", "Remaining", "Reset"); replace these with i18n
lookups by importing the app's translation helper (e.g., useTranslation or t)
and using keys like rateLimit.loading, rateLimit.title, rateLimit.remaining, and
rateLimit.reset in the JSX; add the corresponding entries to the i18n resource
files for all supported locales and update the component to call the translation
function where the literals are used (including the strong title and the two <p>
labels) so no user-visible text remains hardcoded.

</div>
);
}