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
89 changes: 89 additions & 0 deletions src/components/ActivityFeed.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
import { useEffect, useState } from "react";

interface EventType {
id: string;
type: string;
created_at: string;
repo?: {
name: string;
};
}

export default function ActivityFeed({ username }: { username: string }) {
const [events, setEvents] = useState<EventType[]>([]);
const [loading, setLoading] = useState(true);

// 🕒 time ago function
const getTimeAgo = (dateString: string) => {
const diff = Math.floor(
(Date.now() - new Date(dateString).getTime()) / 1000
);

if (diff < 60) return `${diff}s ago`;
if (diff < 3600) return `${Math.floor(diff / 60)}m ago`;
if (diff < 86400) return `${Math.floor(diff / 3600)}h ago`;
return `${Math.floor(diff / 86400)}d ago`;
};

useEffect(() => {
const fetchEvents = async () => {
try {
setLoading(true);

const res = await fetch(
`https://api.github.com/users/${username}/events`
);
const data = await res.json();

setEvents(data);
setLoading(false);
Comment on lines +33 to +39
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail
python - <<'PY'
import json
from urllib.request import urlopen
from urllib.error import HTTPError

def fetch(url):
    try:
        with urlopen(url) as r:
            return r.status, json.load(r)
    except HTTPError as e:
        body = e.read().decode("utf-8")
        try:
            parsed = json.loads(body)
        except Exception:
            parsed = body
        return e.code, parsed

urls = [
    "https://api.github.com/users/octocat/events",
    "https://api.github.com/users/this-user-should-not-exist-xyz/events",
]
for u in urls:
    status, payload = fetch(u)
    print(f"{u} -> status={status}, json_type={type(payload).__name__}")
PY

Repository: GitMetricsLab/github_tracker

Length of output: 2690


🏁 Script executed:

git ls-files | head -20

Repository: GitMetricsLab/github_tracker

Length of output: 567


🏁 Script executed:

fd -t f "ActivityFeed" --case-sensitive

Repository: GitMetricsLab/github_tracker

Length of output: 104


🏁 Script executed:

find . -name "*.tsx" -o -name "*.ts" | grep -i activity

Repository: GitMetricsLab/github_tracker

Length of output: 106


🏁 Script executed:

wc -l src/components/ActivityFeed.tsx

Repository: GitMetricsLab/github_tracker

Length of output: 107


🏁 Script executed:

cat -n src/components/ActivityFeed.tsx

Repository: GitMetricsLab/github_tracker

Length of output: 3146


Guard non-OK and invalid GitHub payloads before setting events.

The code assigns the JSON response directly to events without checking res.ok or validating the payload shape. When GitHub returns an error (e.g., 404, 403), it sends a JSON object, not an event array. This causes a runtime crash when the component later calls events.slice(0, 10).

Proposed fix
-        const data = await res.json();
-
-        setEvents(data);
-        setLoading(false);
+        if (!res.ok) {
+          throw new Error(`GitHub events request failed: ${res.status}`);
+        }
+        const data: unknown = await res.json();
+        setEvents(Array.isArray(data) ? (data as EventType[]) : []);
       } catch (err) {
         console.error(err);
-        setLoading(false);
+        setEvents([]);
+      } finally {
+        setLoading(false);
       }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/components/ActivityFeed.tsx` around lines 33 - 39, The fetch in the
ActivityFeed component should guard non-OK responses and invalid payloads before
calling setEvents: after awaiting fetch(...) check res.ok and then parse JSON;
validate the parsed data is an array (Array.isArray) and only then call
setEvents(data), otherwise setEvents([]) (and optionally set an error state).
Ensure setLoading(false) runs in all paths (finally-equivalent) so the component
doesn't hang, and avoid passing non-array values that would break
events.slice(0, 10).

} catch (err) {
console.error(err);
setLoading(false);
}
};

fetchEvents();

const interval = setInterval(fetchEvents, 30000);
return () => clearInterval(interval);
}, [username]);

return (
<div className="p-4">
<h2 className="text-xl font-bold mb-4 text-center">
Activity Feed
</h2>

{loading ? (
<p className="text-center">Loading...</p>
) : events.length === 0 ? (
<p className="text-center">No activity found</p>
) : (
events.slice(0, 10).map((event) => (
<div
key={event.id}
className="border rounded-lg p-3 mb-3 shadow-sm bg-white dark:bg-gray-700"
>
<p className="text-sm font-semibold">
{event.type === "PushEvent" && "🚀 Commit pushed"}
{event.type === "PullRequestEvent" && "🔀 Pull Request"}
{event.type === "IssuesEvent" && "🐛 Issue"}
{event.type === "WatchEvent" && "⭐ Starred repo"}
{![
"PushEvent",
"PullRequestEvent",
"IssuesEvent",
"WatchEvent",
].includes(event.type) && event.type}
</p>

<p className="text-xs text-gray-500 mt-1">
{event.repo?.name} • {getTimeAgo(event.created_at)}
</p>
Comment on lines +81 to +83
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Avoid rendering undefined for missing repo names.

Some events may not carry repo.name; current UI can show undefined • ....

Proposed fix
-              {event.repo?.name} • {getTimeAgo(event.created_at)}
+              {event.repo?.name ?? "Unknown repository"} • {getTimeAgo(event.created_at)}
📝 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
<p className="text-xs text-gray-500 mt-1">
{event.repo?.name} {getTimeAgo(event.created_at)}
</p>
<p className="text-xs text-gray-500 mt-1">
{event.repo?.name ?? "Unknown repository"} {getTimeAgo(event.created_at)}
</p>
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/components/ActivityFeed.tsx` around lines 81 - 83, The feed currently
renders event.repo?.name directly which can show "undefined • ..." for events
without a repo; update the rendering in ActivityFeed (the <p> block that uses
event.repo?.name and getTimeAgo) to avoid printing undefined by either: a) using
a safe fallback like a default label (e.g., "Unknown repo") when
event.repo?.name is falsy, or b) conditionally rendering the repo segment only
when event.repo?.name exists so the time (getTimeAgo(event.created_at)) still
displays correctly; locate the JSX that references event.repo?.name in
ActivityFeed.tsx and replace it with one of these approaches.

</div>
))
)}
</div>
);
}
14 changes: 14 additions & 0 deletions src/pages/ctivity.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import ActivityFeed from "../components/ActivityFeed";
export default function Activity() {
Comment on lines +1 to +2
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== Candidate page/router files =="
fd -i 'ctivity\.tsx|Activity\.tsx|App\.tsx|router\.tsx|routes\.tsx'

echo
echo "== References to activity routes/imports =="
rg -n -C2 --type=tsx 'ctivity|Activity|/activity|Live GitHub Activity'

Repository: GitMetricsLab/github_tracker

Length of output: 253


🏁 Script executed:

cat -n src/Routes/Router.tsx

Repository: GitMetricsLab/github_tracker

Length of output: 1271


🏁 Script executed:

cat -n src/pages/ctivity.tsx

Repository: GitMetricsLab/github_tracker

Length of output: 573


Remove the orphaned Activity component or register it properly in the router.

The file src/pages/ctivity.tsx is not imported or registered in src/Routes/Router.tsx, making the page unreachable. Additionally, the hardcoded username aparna24bce11388 at line 10 prevents dynamic per-user tracking behavior. Either delete the unused component or register the route (e.g., /activity/:username) and refactor ActivityFeed to accept a username prop from the router.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/pages/ctivity.tsx` around lines 1 - 2, The Activity component in
src/pages/ctivity.tsx is orphaned and embeds a hardcoded username
"aparna24bce11388"; either delete the Activity component file or register it in
the app router (e.g., add a route path like "/activity/:username" in Router.tsx)
and refactor Activity to read the username from route params and pass it into
ActivityFeed (change ActivityFeed usage to accept a username prop instead of the
hardcoded string); ensure the route registration uses the same component name
Activity and that ActivityFeed’s prop signature is updated to accept and use the
dynamic username.

return (
<div className="w-full h-full p-6 bg-gray-50 dark:bg-gray-800">
<div className="max-w-2xl mx-auto">
<h1 className="text-2xl font-bold mb-4 text-center">
Live GitHub Activity
</h1>

<ActivityFeed username="aparna24bce11388" />
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | 🏗️ Heavy lift

Remove hardcoded username from the page contract.

This locks the feature to one account instead of tracking the selected user.

Proposed direction (wire username from parent/router state)
-export default function Activity() {
+export default function Activity({ username }: { username: string }) {
   return (
@@
-        <ActivityFeed username="aparna24bce11388" />
+        <ActivityFeed username={username} />
       </div>
     </div>
   );
 }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/pages/ctivity.tsx` at line 10, The ActivityFeed instance is using a
hardcoded username ("aparna24bce11388"); update the page to obtain the selected
username from parent or router state instead of hardcoding: replace the literal
in the ActivityFeed JSX with a dynamic value (e.g., a prop like selectedUser or
router query value from useRouter()) and ensure the page component
accepts/derives that username and passes it into ActivityFeed (look for
ActivityFeed and the page component in ctivity.tsx to change the prop wiring).

</div>
</div>
);
}
Loading