Skip to content

feat: search oraganization and view basic details#49

Open
kunal-bunkar wants to merge 1 commit intoAOSSIE-Org:mainfrom
kunal-bunkar:feat/search-organization
Open

feat: search oraganization and view basic details#49
kunal-bunkar wants to merge 1 commit intoAOSSIE-Org:mainfrom
kunal-bunkar:feat/search-organization

Conversation

@kunal-bunkar
Copy link

@kunal-bunkar kunal-bunkar commented Mar 22, 2026

Addressed Issues:

Fixes #48

Screenshots/Recordings:

Landing page
image
Dashboard page
image

Additional Notes:

-Routes: / (landing + search), /org/:login (detail); unknown paths redirect to /
-API: GitHub REST API from the browser only (fetch to api.github.com) — no backend. Search uses search/users with type:org; if there are no results, the app tries GET /orgs/:login for an exact handle.
-How to verify: npm install → npm run dev → search e.g. aossie or vercel → confirm detail loads and Back to search returns home.
-Rate limits: Unauthenticated GitHub API limits apply (documented in UI hint on landing).

Checklist

  • My code follows the project's code style and conventions
  • I have made corresponding changes to the documentation
  • My changes generate no new warnings or errors
  • I have joined the Discord server and I will share a link to this PR with the project maintainers there
  • I have read the Contributing Guidelines

Summary by CodeRabbit

Release Notes

  • New Features

    • Search GitHub organizations by name with instant profile navigation
    • View detailed organization information including avatars, repository counts, and follower statistics
    • Client-side routing for seamless navigation between search results and organization profiles
  • Styling

    • Integrated Tailwind CSS utility framework
    • Applied modern dark theme with gradient background effects

@coderabbitai
Copy link

coderabbitai bot commented Mar 22, 2026

Walkthrough

This pull request introduces a GitHub Organization Explorer feature by setting up client-side routing, adding GitHub API integration, creating search and detail pages, and integrating Tailwind CSS for styling. New dependencies (react-router-dom, Tailwind packages) and configuration changes enable the routed application structure.

Changes

Cohort / File(s) Summary
Dependency & Build Configuration
package.json, vite.config.ts
Added react-router-dom to dependencies and @tailwindcss/vite, tailwindcss to devDependencies. Integrated Tailwind CSS plugin into Vite's plugin pipeline.
Styling System Migration
src/App.css, src/index.css
Removed #root styling from App.css. Migrated index.css to Tailwind directives with custom theme, dark background, gradient overlays, and typography utilities.
Routing & App Structure
src/App.tsx
Replaced static content with BrowserRouter wrapper and Routes configuration for / (LandingPage), /org/:login (OrgDetailPage), and catch-all redirect to /.
GitHub API Module
src/api/github.ts
New module providing searchOrganizations() and fetchOrganization() functions with typed interfaces (OrgSearchResult, OrgSearchItem, GitHubOrg), error handling, and GitHub API media type headers.
Landing Page Component
src/pages/LandingPage.tsx
New search interface component managing query input, loading/error states, and organization results list. Handles form submission, API calls, conditional single-result navigation, and fallback organization fetch attempts.
Organization Detail Page Component
src/pages/OrgDetailPage.tsx
New detail view component reading login route parameter, fetching organization data with cancellation handling, and rendering avatar, profile info, formatted metadata, repository/follower counts, and GitHub link.

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant LandingPage
    participant GitHubAPI
    participant Router
    participant OrgDetailPage

    User->>LandingPage: Enter search query & submit
    LandingPage->>LandingPage: Validate input
    LandingPage->>GitHubAPI: searchOrganizations(query)
    GitHubAPI-->>LandingPage: Results array
    alt Single Result Found
        LandingPage->>Router: Navigate to /org/{login}
        Router->>OrgDetailPage: Load page
        OrgDetailPage->>GitHubAPI: fetchOrganization(login)
        GitHubAPI-->>OrgDetailPage: Organization details
        OrgDetailPage-->>User: Display org profile
    else Multiple Results
        LandingPage-->>User: Display results list
        User->>LandingPage: Select organization
        LandingPage->>Router: Navigate to /org/{login}
        Router->>OrgDetailPage: Load page
        OrgDetailPage->>GitHubAPI: fetchOrganization(login)
        GitHubAPI-->>OrgDetailPage: Organization details
        OrgDetailPage-->>User: Display org profile
    else No Results
        LandingPage->>GitHubAPI: fetchOrganization(query)
        GitHubAPI-->>OrgDetailPage: Organization details
        OrgDetailPage-->>User: Display org profile
    end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~30 minutes

Possibly related PRs

Suggested labels

Typescript Lang

Suggested reviewers

  • Zahnentferner

Poem

🐰 A rabbit hops with glee so bright,
Organizations now in sight!
Routes and forms and API calls,
Tailwind dances through the halls,
OrgExplorer's magic we install!

🚥 Pre-merge checks | ✅ 4
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title 'feat: search organization and view basic details' accurately describes the main changes in the pull request: adding organization search functionality and displaying organization details.
Linked Issues check ✅ Passed The pull request successfully implements all core requirements from issue #47: organization search input, GitHub API integration, organization details display (avatar, name, description, followers, repos), loading/error handling, and improved UX with result clearing.
Out of Scope Changes check ✅ Passed All changes are directly aligned with issue #47 requirements: API integration, routing, search page, detail page, and styling updates. No out-of-scope modifications detected.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 5

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/pages/LandingPage.tsx`:
- Around line 58-64: The component LandingPage currently has hardcoded
user-facing strings (e.g., "OrgExplorer", the paragraph "Search public GitHub
organizations by name or handle. Open a profile for a quick summary.", and other
strings like "Pick an organization" and "Organization on GitHub"); replace these
with i18n resource keys by adding entries to your locale/resource file and using
your project's translation helper in LandingPage (e.g., import and call the
translation function such as useTranslation/t or similar) to render
t('landing.title'), t('landing.description'), t('landing.pickOrganization'),
t('landing.organizationOnGithub'), etc.; ensure you update all occurrences
referenced in the comment (including strings around lines 58–64, 109–110,
128–133), add the new keys to the resource file(s), and run/localize tests to
confirm rendering works.
- Around line 39-40: The fallback navigation uses the raw trimmed query q which
can break routes for spaces/special chars; update the code that calls
navigate(`/org/${q}`) (after await fetchOrganization(q)) to URL-encode the query
using encodeURIComponent(q) so the path is safe (e.g.,
navigate(`/org/${encodeURIComponent(q)}`)); keep using the same q and
fetchOrganization call but ensure the encoded value is used only for the
navigation.
- Around line 16-48: handleSubmit can start overlapping requests which allow
stale responses to overwrite newer results; create and use an AbortController
per submit to cancel any previous in-flight request before starting a new one,
pass controller.signal into searchOrganizations (update src/api/github.ts to
accept an optional AbortSignal and forward it to fetch) and into
fetchOrganization if it is used for the fallback, store the current controller
(e.g., in a ref) and call abort() before creating a new controller, and ensure
you clean up the controller in the finally block so setResults/setError come
from the latest non-aborted response.

In `@src/pages/OrgDetailPage.tsx`:
- Around line 97-106: In OrgDetailPage, externalize all user-visible literal
strings (e.g., the "@{org.login}" prefix, labels like "Location", "Website", "X
/ Twitter", "Public repositories", "Followers", "Created", "View on GitHub", and
messages like "Missing organization.") into your i18n resource files and replace
the inline literals in the JSX with lookups (e.g., via your project's
translation hook/utility). Update the OrgDetailPage component where h1, the
username p, description p, and the label/value blocks render text to use
translation keys (create descriptive keys such as org.title, org.handlePrefix,
org.locationLabel, org.websiteLabel, org.twitterLabel, org.publicReposLabel,
org.followersLabel, org.createdLabel, org.viewOnGithub, org.missingOrg) and wire
them to the resource files for all languages.
- Around line 36-61: Replace the cancelled-flag pattern with an AbortController
to actually cancel the network request: create an AbortController inside the
useEffect, pass its signal to fetchOrganization (update fetchOrganization to
accept an optional signal parameter), and on cleanup call controller.abort();
keep the existing guards that avoid state updates (setOrg, setError, setLoading)
if the component unmounted or the request was aborted. Ensure fetchOrganization
forwards the signal to fetch/axios and throws or rejects appropriately so the
catch block handles aborts without updating state.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 45342133-cc1a-4465-b8b4-5f7c4a816901

📥 Commits

Reviewing files that changed from the base of the PR and between 1cfc3e7 and 8d36cda.

⛔ Files ignored due to path filters (1)
  • package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (8)
  • package.json
  • src/App.css
  • src/App.tsx
  • src/api/github.ts
  • src/index.css
  • src/pages/LandingPage.tsx
  • src/pages/OrgDetailPage.tsx
  • vite.config.ts
💤 Files with no reviewable changes (1)
  • src/App.css

Comment on lines +16 to +48
async function handleSubmit(e: FormEvent) {
e.preventDefault();
const q = query.trim();
if (!q) {
setError("Enter an organization name or login.");
setResults(null);
return;
}

setLoading(true);
setError(null);
setResults(null);

try {
const data = await searchOrganizations(q);
if (data.items.length > 0) {
setResults(data.items);
if (data.items.length === 1) {
navigate(`/org/${data.items[0].login}`);
}
return;
}

await fetchOrganization(q);
navigate(`/org/${q}`);
} catch (err) {
const message =
err instanceof Error ? err.message : "Something went wrong.";
setError(message);
} finally {
setLoading(false);
}
}
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

Missing request cancellation for overlapping searches.

The linked issue #47 explicitly requires "cancel overlapping requests so results remain current." Currently, if a user submits multiple searches quickly, stale responses could overwrite fresher results. Consider using AbortController to cancel in-flight requests when a new search begins.

🛠️ Suggested approach
+import { useRef } from "react";
+// ...
+
 export function LandingPage() {
   const navigate = useNavigate();
+  const abortRef = useRef<AbortController | null>(null);
   const [query, setQuery] = useState("");
   // ...
 
   async function handleSubmit(e: FormEvent) {
     e.preventDefault();
     const q = query.trim();
     if (!q) {
       setError("Enter an organization name or login.");
       setResults(null);
       return;
     }
 
+    // Cancel any in-flight request
+    abortRef.current?.abort();
+    abortRef.current = new AbortController();
+
     setLoading(true);
     setError(null);
     setResults(null);
 
     try {
-      const data = await searchOrganizations(q);
+      const data = await searchOrganizations(q, abortRef.current.signal);
       // ...

This requires updating searchOrganizations in src/api/github.ts to accept an optional AbortSignal and pass it to fetch.

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

In `@src/pages/LandingPage.tsx` around lines 16 - 48, handleSubmit can start
overlapping requests which allow stale responses to overwrite newer results;
create and use an AbortController per submit to cancel any previous in-flight
request before starting a new one, pass controller.signal into
searchOrganizations (update src/api/github.ts to accept an optional AbortSignal
and forward it to fetch) and into fetchOrganization if it is used for the
fallback, store the current controller (e.g., in a ref) and call abort() before
creating a new controller, and ensure you clean up the controller in the finally
block so setResults/setError come from the latest non-aborted response.

Comment on lines +39 to +40
await fetchOrganization(q);
navigate(`/org/${q}`);
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

URL-encode the fallback navigation path.

When no search results are found, the raw trimmed query q is used directly in the navigation path. If the query contains special characters or spaces, this could cause routing issues.

🛡️ Proposed fix
       await fetchOrganization(q);
-      navigate(`/org/${q}`);
+      navigate(`/org/${encodeURIComponent(q)}`);
📝 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
await fetchOrganization(q);
navigate(`/org/${q}`);
await fetchOrganization(q);
navigate(`/org/${encodeURIComponent(q)}`);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/pages/LandingPage.tsx` around lines 39 - 40, The fallback navigation uses
the raw trimmed query q which can break routes for spaces/special chars; update
the code that calls navigate(`/org/${q}`) (after await fetchOrganization(q)) to
URL-encode the query using encodeURIComponent(q) so the path is safe (e.g.,
navigate(`/org/${encodeURIComponent(q)}`)); keep using the same q and
fetchOrganization call but ensure the encoded value is used only for the
navigation.

Comment on lines +58 to +64
<h1 className="mb-4 text-4xl font-bold tracking-tight text-white sm:text-5xl">
OrgExplorer
</h1>
<p className="mx-auto max-w-md text-pretty text-sm leading-relaxed text-zinc-400 sm:text-base">
Search public GitHub organizations by name or handle. Open a profile
for a quick summary.
</p>
Copy link

Choose a reason for hiding this comment

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

🧹 Nitpick | 🔵 Trivial

User-visible strings are hardcoded.

Per coding guidelines, user-visible strings should be externalized to resource files for internationalization (i18n). Strings like "OrgExplorer", "Search public GitHub organizations...", "Pick an organization", "Organization on GitHub", etc. are currently hardcoded throughout the component.

Also applies to: 109-110, 128-133

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

In `@src/pages/LandingPage.tsx` around lines 58 - 64, The component LandingPage
currently has hardcoded user-facing strings (e.g., "OrgExplorer", the paragraph
"Search public GitHub organizations by name or handle. Open a profile for a
quick summary.", and other strings like "Pick an organization" and "Organization
on GitHub"); replace these with i18n resource keys by adding entries to your
locale/resource file and using your project's translation helper in LandingPage
(e.g., import and call the translation function such as useTranslation/t or
similar) to render t('landing.title'), t('landing.description'),
t('landing.pickOrganization'), t('landing.organizationOnGithub'), etc.; ensure
you update all occurrences referenced in the comment (including strings around
lines 58–64, 109–110, 128–133), add the new keys to the resource file(s), and
run/localize tests to confirm rendering works.

Comment on lines +36 to +61
useEffect(() => {
let cancelled = false;

fetchOrganization(login)
.then((data) => {
if (!cancelled) {
setOrg(data);
setError(null);
}
})
.catch((err) => {
if (!cancelled) {
setError(
err instanceof Error ? err.message : "Failed to load organization.",
);
setOrg(null);
}
})
.finally(() => {
if (!cancelled) setLoading(false);
});

return () => {
cancelled = true;
};
}, [login]);
Copy link

Choose a reason for hiding this comment

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

🧹 Nitpick | 🔵 Trivial

Good cancellation pattern, but consider using AbortController for efficiency.

The cancelled flag correctly prevents state updates after unmount or when login changes. However, the fetch request itself continues to completion. For improved efficiency (especially on slow networks), you could use AbortController to actually cancel the HTTP request.

♻️ Optional enhancement using AbortController
   useEffect(() => {
-    let cancelled = false;
+    const controller = new AbortController();
 
-    fetchOrganization(login)
+    fetchOrganization(login, controller.signal)
       .then((data) => {
-        if (!cancelled) {
-          setOrg(data);
-          setError(null);
-        }
+        setOrg(data);
+        setError(null);
       })
       .catch((err) => {
-        if (!cancelled) {
-          setError(
-            err instanceof Error ? err.message : "Failed to load organization.",
-          );
-          setOrg(null);
-        }
+        if (err.name === "AbortError") return;
+        setError(
+          err instanceof Error ? err.message : "Failed to load organization.",
+        );
+        setOrg(null);
       })
       .finally(() => {
-        if (!cancelled) setLoading(false);
+        setLoading(false);
       });
 
     return () => {
-      cancelled = true;
+      controller.abort();
     };
   }, [login]);

This requires updating fetchOrganization in src/api/github.ts to accept an optional signal parameter.

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

In `@src/pages/OrgDetailPage.tsx` around lines 36 - 61, Replace the cancelled-flag
pattern with an AbortController to actually cancel the network request: create
an AbortController inside the useEffect, pass its signal to fetchOrganization
(update fetchOrganization to accept an optional signal parameter), and on
cleanup call controller.abort(); keep the existing guards that avoid state
updates (setOrg, setError, setLoading) if the component unmounted or the request
was aborted. Ensure fetchOrganization forwards the signal to fetch/axios and
throws or rejects appropriately so the catch block handles aborts without
updating state.

Comment on lines +97 to +106
<h1 className="text-2xl font-bold tracking-tight text-white sm:text-3xl">
{org.name || org.login}
</h1>
<p className="mt-1 font-medium text-emerald-400/90">
@{org.login}
</p>
{org.description && (
<p className="mt-4 text-sm leading-relaxed text-zinc-400">
{org.description}
</p>
Copy link

Choose a reason for hiding this comment

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

🧹 Nitpick | 🔵 Trivial

User-visible strings should be externalized for i18n.

Consistent with the feedback on LandingPage.tsx, labels such as "Location", "Website", "X / Twitter", "Public repositories", "Followers", "Created", "View on GitHub", "Missing organization.", etc. should be externalized to resource files for internationalization support.

Also applies to: 115-116, 121-134, 140-151, 153-162

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

In `@src/pages/OrgDetailPage.tsx` around lines 97 - 106, In OrgDetailPage,
externalize all user-visible literal strings (e.g., the "@{org.login}" prefix,
labels like "Location", "Website", "X / Twitter", "Public repositories",
"Followers", "Created", "View on GitHub", and messages like "Missing
organization.") into your i18n resource files and replace the inline literals in
the JSX with lookups (e.g., via your project's translation hook/utility). Update
the OrgDetailPage component where h1, the username p, description p, and the
label/value blocks render text to use translation keys (create descriptive keys
such as org.title, org.handlePrefix, org.locationLabel, org.websiteLabel,
org.twitterLabel, org.publicReposLabel, org.followersLabel, org.createdLabel,
org.viewOnGithub, org.missingOrg) and wire them to the resource files for all
languages.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

FEATURE: GitHub org search and view basic detail

1 participant