From 3f0f462eb127c80820aeb8907b3813b29211376c Mon Sep 17 00:00:00 2001 From: Yash Raj Date: Sat, 11 Oct 2025 09:39:05 +0530 Subject: [PATCH 01/15] Enable multi-language repo search and selection Updated the hero component to allow users to select multiple programming languages via checkboxes, both from main and 'other' languages. Modified the repo page logic to accept and process multiple languages, fetching and merging results from GitHub for each selected language. This enhances the search experience by supporting multi-language queries. (commit desc by copilot) --- src/app/(public)/_components/hero.tsx | 87 ++++++++++++++++------ src/app/(public)/repos/[language]/page.tsx | 54 +++++++++----- 2 files changed, 101 insertions(+), 40 deletions(-) diff --git a/src/app/(public)/_components/hero.tsx b/src/app/(public)/_components/hero.tsx index 7f2ad7c..f3b9723 100644 --- a/src/app/(public)/_components/hero.tsx +++ b/src/app/(public)/_components/hero.tsx @@ -1,11 +1,10 @@ 'use client'; import { useRouter } from 'next/navigation'; +import { useMemo, useState } from 'react'; import { Search } from 'lucide-react'; -import { LanguageButton } from './language-button'; import { Button } from './button'; -import Link from 'next/link'; import { sortByName } from '@/lib/utils'; import languages from '@/assets/languages.json'; @@ -17,11 +16,34 @@ const { main: mainLanguages, others: otherLanguages } = languages; export function Hero() { const router = useRouter(); + // Track selected languages as a string array + const [selected, setSelected] = useState([]); + + const toggleLanguage = (language: string) => { + setSelected(prev => + prev.includes(language) ? prev.filter(l => l !== language) : [...prev, language] + ); + }; + + const sortedOthers = useMemo(() => [...otherLanguages].sort(sortByName), []); + function handleSearch(e: React.FormEvent) { + e.preventDefault(); const formData = new FormData(e.target as HTMLFormElement); - const lang = formData.get('search') as string; - if (lang.trim() === '') return; - router.push(`/repos/${lang}`); + let chosen = selected; + + // Fallback: if no checkbox selected, use the single input value + if (chosen.length === 0) { + const typed = String(formData.get('search') || '').trim(); + if (typed) { + chosen = [typed]; + } + } + + if (chosen.length === 0) return; // nothing to search + + const csv = chosen.map(l => l.toLowerCase()).join(','); + router.push(`/repos?l=${encodeURIComponent(csv)}`); } return ( @@ -39,7 +61,7 @@ export function Hero() {

- Or select the programming language you would like to find + Or select the programming languages you would like to find repositories for.

+
- {mainLanguages.map(language => ( - - ))} + {mainLanguages.map(language => { + const id = `lang-${language}`; + const checked = selected.includes(language); + return ( + + ); + })}
+
diff --git a/src/app/(public)/repos/[language]/page.tsx b/src/app/(public)/repos/[language]/page.tsx index f8349b8..37d8d29 100644 --- a/src/app/(public)/repos/[language]/page.tsx +++ b/src/app/(public)/repos/[language]/page.tsx @@ -105,16 +105,7 @@ async function getRepos( ? `stars:<${endStars}` : ''; - const apiUrl = new URL('https://api.github.com/search/repositories'); - apiUrl.searchParams.set('page', page.toString()); - apiUrl.searchParams.set('per_page', '21'); - apiUrl.searchParams.set('sort', sort.toString()); - apiUrl.searchParams.set('order', order.toString()); - apiUrl.searchParams.set( - 'q', - `topic:hacktoberfest language:${language} ${searchQuery} ${starsQuery}` - ); - + const languages = language.split(',').map(l => l.trim()); // split multi-language const headers: HeadersInit = { Accept: 'application/vnd.github.mercy-preview+json' }; @@ -136,25 +127,50 @@ async function getRepos( headers.Authorization = `Bearer ${env.AUTH_GITHUB_TOKEN}`; } - const res = await fetch(apiUrl, { headers }); - if (!res.ok) return undefined; - - const repos = (await res.json()) as RepoData; const reports = await getReportedRepos(); + let allRepos: RepoItem[] = []; + + // fetch github repos for each language and merge + for (const lang of languages) { + const apiUrl = new URL('https://api.github.com/search/repositories'); + apiUrl.searchParams.set('page', page.toString()); + apiUrl.searchParams.set('per_page', '21'); + apiUrl.searchParams.set('sort', sort.toString()); + apiUrl.searchParams.set('order', order.toString()); + apiUrl.searchParams.set( + 'q', + `topic:hacktoberfest language:${lang} ${searchQuery} ${starsQuery}` + ); + + const res = await fetch(apiUrl, { headers }); + if (!res.ok) continue; + + const reposData: RepoData = await res.json(); + const filteredItems = reposData.items.filter( + (repo: RepoItem) => !repo.archived && !reports.find(r => r.repoId === repo.id) + ); + + allRepos = allRepos.concat(filteredItems); + } - repos.items = repos.items.filter((repo: RepoItem) => { - return !repo.archived && !reports.find(report => report.repoId === repo.id); - }); + if (allRepos.length < 1) return undefined; - if (!Array.isArray(repos.items) || repos.items?.length < 1) return undefined; + // sort merged repos by updated date descending + allRepos.sort( + (a, b) => new Date(b.updated_at).getTime() - new Date(a.updated_at).getTime() + ); return { page: +page.toString(), languageName: language, - repos + repos: { + ...{ total_count: allRepos.length, incomplete_results: false }, + items: allRepos + } }; } + async function getReportedRepos() { const reports = await db .select() From bafa1dde20f4682599e1b2995f07f005f9829615 Mon Sep 17 00:00:00 2001 From: Yash Raj Date: Sat, 11 Oct 2025 14:39:15 +0530 Subject: [PATCH 02/15] Update src/app/(public)/_components/hero.tsx Co-authored-by: Usman Sabuwala <51731966+max-programming@users.noreply.github.com> --- src/app/(public)/_components/hero.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/app/(public)/_components/hero.tsx b/src/app/(public)/_components/hero.tsx index f3b9723..0ea156e 100644 --- a/src/app/(public)/_components/hero.tsx +++ b/src/app/(public)/_components/hero.tsx @@ -76,7 +76,7 @@ export function Hero() {

- Or select the programming languages you would like to find + Or select one or more programming languages you would like to find repositories for.

From 7a276eceabef447ebe7be18442fdd5a7d3c1977b Mon Sep 17 00:00:00 2001 From: Yash Raj Date: Sat, 11 Oct 2025 21:58:43 +0530 Subject: [PATCH 03/15] fix: as per req fixed the requested changes - added search button below - directory move from `/(public)/repos/[language]/_components` to `/(public)/repos/_components` --- src/app/(public)/_components/hero.tsx | 52 +++---- src/app/(public)/repos/[language]/page.tsx | 40 +++--- .../_components/pagination.tsx | 0 .../_components/repo-card.tsx | 0 .../_components/report-button.tsx | 0 .../_components/scroll-to-top.tsx | 0 .../{[language] => }/_components/sorter.tsx | 0 .../_components/stars-filter.tsx | 0 src/app/(public)/repos/page.tsx | 135 ++++++++++++++++++ 9 files changed, 186 insertions(+), 41 deletions(-) rename src/app/(public)/repos/{[language] => }/_components/pagination.tsx (100%) rename src/app/(public)/repos/{[language] => }/_components/repo-card.tsx (100%) rename src/app/(public)/repos/{[language] => }/_components/report-button.tsx (100%) rename src/app/(public)/repos/{[language] => }/_components/scroll-to-top.tsx (100%) rename src/app/(public)/repos/{[language] => }/_components/sorter.tsx (100%) rename src/app/(public)/repos/{[language] => }/_components/stars-filter.tsx (100%) create mode 100644 src/app/(public)/repos/page.tsx diff --git a/src/app/(public)/_components/hero.tsx b/src/app/(public)/_components/hero.tsx index f3b9723..e752ffb 100644 --- a/src/app/(public)/_components/hero.tsx +++ b/src/app/(public)/_components/hero.tsx @@ -51,32 +51,10 @@ export function Hero() {

- Search your language + Search your language(s)

-
-
-
- -
- -
-

- Or select the programming languages you would like to find + Or select one or more programming languages you would like to find repositories for.

@@ -99,7 +77,7 @@ export function Hero() { })}
-
+
@@ -128,6 +106,30 @@ export function Hero() { })}
+ + {/* Search form moved below language selection */} +
+
+
+ +
+ +
+
diff --git a/src/app/(public)/repos/[language]/page.tsx b/src/app/(public)/repos/[language]/page.tsx index 37d8d29..2c8afed 100644 --- a/src/app/(public)/repos/[language]/page.tsx +++ b/src/app/(public)/repos/[language]/page.tsx @@ -2,11 +2,11 @@ import { env } from '@/env.mjs'; import { notFound } from 'next/navigation'; import { capitalize } from '@/lib/utils'; import { Header } from '@/app/(public)/_components/header'; -import { ScrollToTop } from './_components/scroll-to-top'; -import { RepoCard } from './_components/repo-card'; -import { Sorter } from './_components/sorter'; -import { StarsFilter } from './_components/stars-filter'; -import { Pagination } from './_components/pagination'; +import { ScrollToTop } from '../_components/scroll-to-top'; +import { RepoCard } from '../_components/repo-card'; +import { Sorter } from '../_components/sorter'; +import { StarsFilter } from '../_components/stars-filter'; +import { Pagination } from '../_components/pagination'; import type { RepoData, RepoItem, RepoResponse, SearchParams } from '@/types'; import type { Metadata } from 'next'; import { auth } from '@/auth'; @@ -105,7 +105,16 @@ async function getRepos( ? `stars:<${endStars}` : ''; - const languages = language.split(',').map(l => l.trim()); // split multi-language + const apiUrl = new URL('https://api.github.com/search/repositories'); + apiUrl.searchParams.set('page', page.toString()); + apiUrl.searchParams.set('per_page', '21'); + apiUrl.searchParams.set('sort', sort.toString()); + apiUrl.searchParams.set('order', order.toString()); + apiUrl.searchParams.set( + 'q', + `topic:hacktoberfest language:${language} ${searchQuery} ${starsQuery}` + ); + const headers: HeadersInit = { Accept: 'application/vnd.github.mercy-preview+json' }; @@ -127,6 +136,10 @@ async function getRepos( headers.Authorization = `Bearer ${env.AUTH_GITHUB_TOKEN}`; } + const res = await fetch(apiUrl, { headers }); + if (!res.ok) return undefined; + + const repos = (await res.json()) as RepoData; const reports = await getReportedRepos(); let allRepos: RepoItem[] = []; @@ -153,24 +166,19 @@ async function getRepos( allRepos = allRepos.concat(filteredItems); } - if (allRepos.length < 1) return undefined; + repos.items = repos.items.filter((repo: RepoItem) => { + return !repo.archived && !reports.find(report => report.repoId === repo.id); + }); - // sort merged repos by updated date descending - allRepos.sort( - (a, b) => new Date(b.updated_at).getTime() - new Date(a.updated_at).getTime() - ); + if (!Array.isArray(repos.items) || repos.items?.length < 1) return undefined; return { page: +page.toString(), languageName: language, - repos: { - ...{ total_count: allRepos.length, incomplete_results: false }, - items: allRepos - } + repos }; } - async function getReportedRepos() { const reports = await db .select() diff --git a/src/app/(public)/repos/[language]/_components/pagination.tsx b/src/app/(public)/repos/_components/pagination.tsx similarity index 100% rename from src/app/(public)/repos/[language]/_components/pagination.tsx rename to src/app/(public)/repos/_components/pagination.tsx diff --git a/src/app/(public)/repos/[language]/_components/repo-card.tsx b/src/app/(public)/repos/_components/repo-card.tsx similarity index 100% rename from src/app/(public)/repos/[language]/_components/repo-card.tsx rename to src/app/(public)/repos/_components/repo-card.tsx diff --git a/src/app/(public)/repos/[language]/_components/report-button.tsx b/src/app/(public)/repos/_components/report-button.tsx similarity index 100% rename from src/app/(public)/repos/[language]/_components/report-button.tsx rename to src/app/(public)/repos/_components/report-button.tsx diff --git a/src/app/(public)/repos/[language]/_components/scroll-to-top.tsx b/src/app/(public)/repos/_components/scroll-to-top.tsx similarity index 100% rename from src/app/(public)/repos/[language]/_components/scroll-to-top.tsx rename to src/app/(public)/repos/_components/scroll-to-top.tsx diff --git a/src/app/(public)/repos/[language]/_components/sorter.tsx b/src/app/(public)/repos/_components/sorter.tsx similarity index 100% rename from src/app/(public)/repos/[language]/_components/sorter.tsx rename to src/app/(public)/repos/_components/sorter.tsx diff --git a/src/app/(public)/repos/[language]/_components/stars-filter.tsx b/src/app/(public)/repos/_components/stars-filter.tsx similarity index 100% rename from src/app/(public)/repos/[language]/_components/stars-filter.tsx rename to src/app/(public)/repos/_components/stars-filter.tsx diff --git a/src/app/(public)/repos/page.tsx b/src/app/(public)/repos/page.tsx new file mode 100644 index 0000000..39e9dc4 --- /dev/null +++ b/src/app/(public)/repos/page.tsx @@ -0,0 +1,135 @@ +import { env } from '@/env.mjs'; +import { notFound } from 'next/navigation'; +import { Header } from '@/app/(public)/_components/header'; +import { ScrollToTop } from './_components/scroll-to-top'; +import { RepoCard } from './_components/repo-card'; +import { Sorter } from './_components/sorter'; +import { StarsFilter } from './_components/stars-filter'; +import { Pagination } from './_components/pagination'; +import { auth } from '@/auth'; +import { db } from '@/lib/db/connection'; +import { accountsTable, reportsTable } from '@/lib/db/migrations/schema'; +import { eq } from 'drizzle-orm'; +import type { RepoResponse, RepoData, RepoItem, SearchParams } from '@/types'; + +export default async function ReposPage({ searchParams }: { searchParams: Promise }) { + const sp = await searchParams; + const raw = (sp as any).l ?? (sp as any).langs ?? ''; + const langs = (Array.isArray(raw) ? raw : raw.toString().split(',')) + .map((s: string) => decodeURIComponent(s.trim())) + .filter(Boolean); + + const reposRes = await getRepos(langs, sp); + if (!reposRes) notFound(); + + const { repos, page } = reposRes; + + return ( + <> +
+ +
+
+
+ + +
+ {repos.items.map(repo => ( + + ))} +
+
+ +
+
+ + ); +} + +async function getRepos( + languages: string[], + searchParams: SearchParams +): Promise { + const session = await auth(); + const { + p: page = '1', + s: sort = 'updated', + o: order = 'desc', + q: searchQuery = '', + startStars = '1', + endStars = '' + } = searchParams; + + const starsQuery = + startStars && endStars + ? `stars:${startStars}..${endStars}` + : startStars && !endStars + ? `stars:>${startStars}` + : !startStars && endStars + ? `stars:<${endStars}` + : ''; + + const combinedLangs = languages.map(l => `language:${l}`).join(' '); + + const apiUrl = new URL('https://api.github.com/search/repositories'); + apiUrl.searchParams.set('page', page.toString()); + apiUrl.searchParams.set('per_page', '21'); + apiUrl.searchParams.set('sort', sort.toString()); + apiUrl.searchParams.set('order', order.toString()); + apiUrl.searchParams.set( + 'q', + `topic:hacktoberfest ${combinedLangs} ${searchQuery} ${starsQuery}` + ); + + const headers: HeadersInit = { + Accept: 'application/vnd.github.mercy-preview+json' + }; + + const userId = session?.user?.id; + + if (userId) { + const [account] = await db + .select() + .from(accountsTable) + .where(eq(accountsTable.userId, userId)) + .limit(1); + + if (account && account.access_token) { + headers.Authorization = `Bearer ${account.access_token}`; + } else if (env.AUTH_GITHUB_TOKEN) { + headers.Authorization = `Bearer ${env.AUTH_GITHUB_TOKEN}`; + } + } else if (env.AUTH_GITHUB_TOKEN) { + headers.Authorization = `Bearer ${env.AUTH_GITHUB_TOKEN}`; + } + + const res = await fetch(apiUrl, { headers }); + if (!res.ok) return undefined; + + const repos = (await res.json()) as RepoData; + const reports = await getReportedRepos(); + + repos.items = repos.items.filter((repo: RepoItem) => { + return !repo.archived && !reports.find(report => report.repoId === repo.id); + }); + + return { + page: +page.toString(), + languageName: languages.join(', '), + repos + }; +} + +async function getReportedRepos() { + const reports = await db + .select() + .from(reportsTable) + .where(eq(reportsTable.valid, false)) + .limit(100); + + return reports; +} From 6e7bd4b0c0740873dcbe236c574b7b13e5239dfa Mon Sep 17 00:00:00 2001 From: Yash Raj Date: Sat, 11 Oct 2025 21:59:00 +0530 Subject: [PATCH 04/15] Update page.tsx --- src/app/(public)/repos/[language]/page.tsx | 24 ---------------------- 1 file changed, 24 deletions(-) diff --git a/src/app/(public)/repos/[language]/page.tsx b/src/app/(public)/repos/[language]/page.tsx index 2c8afed..d5c0bbb 100644 --- a/src/app/(public)/repos/[language]/page.tsx +++ b/src/app/(public)/repos/[language]/page.tsx @@ -141,30 +141,6 @@ async function getRepos( const repos = (await res.json()) as RepoData; const reports = await getReportedRepos(); - let allRepos: RepoItem[] = []; - - // fetch github repos for each language and merge - for (const lang of languages) { - const apiUrl = new URL('https://api.github.com/search/repositories'); - apiUrl.searchParams.set('page', page.toString()); - apiUrl.searchParams.set('per_page', '21'); - apiUrl.searchParams.set('sort', sort.toString()); - apiUrl.searchParams.set('order', order.toString()); - apiUrl.searchParams.set( - 'q', - `topic:hacktoberfest language:${lang} ${searchQuery} ${starsQuery}` - ); - - const res = await fetch(apiUrl, { headers }); - if (!res.ok) continue; - - const reposData: RepoData = await res.json(); - const filteredItems = reposData.items.filter( - (repo: RepoItem) => !repo.archived && !reports.find(r => r.repoId === repo.id) - ); - - allRepos = allRepos.concat(filteredItems); - } repos.items = repos.items.filter((repo: RepoItem) => { return !repo.archived && !reports.find(report => report.repoId === repo.id); From f907abc789c86f2258bc79ee0d8ed85f012c2a2a Mon Sep 17 00:00:00 2001 From: Yash Raj Date: Mon, 13 Oct 2025 08:19:55 +0530 Subject: [PATCH 05/15] send array as url search Params - Replaced CSV params with Array - Moved the search bar up, and the button where it was. --- src/app/(public)/_components/hero.tsx | 95 ++++++++++++++++----------- src/app/(public)/repos/page.tsx | 10 +-- 2 files changed, 64 insertions(+), 41 deletions(-) diff --git a/src/app/(public)/_components/hero.tsx b/src/app/(public)/_components/hero.tsx index e752ffb..2c17261 100644 --- a/src/app/(public)/_components/hero.tsx +++ b/src/app/(public)/_components/hero.tsx @@ -30,20 +30,22 @@ export function Hero() { function handleSearch(e: React.FormEvent) { e.preventDefault(); const formData = new FormData(e.target as HTMLFormElement); - let chosen = selected; + let chosenLanguages = selected; // Fallback: if no checkbox selected, use the single input value - if (chosen.length === 0) { - const typed = String(formData.get('search') || '').trim(); - if (typed) { - chosen = [typed]; - } - } + if (chosenLanguages.length === 0) { + const typed = String(formData.get('search') || '').trim(); + if (typed) { + chosenLanguages = [typed]; + } +} - if (chosen.length === 0) return; // nothing to search +if (chosenLanguages.length === 0) return; // nothing to search - const csv = chosen.map(l => l.toLowerCase()).join(','); - router.push(`/repos?l=${encodeURIComponent(csv)}`); +const params = new URLSearchParams(); +chosenLanguages.forEach(lang => params.append('l', lang.toLowerCase())); + +router.push(`/repos?${params.toString()}`); } return ( @@ -53,6 +55,28 @@ export function Hero() {

Search your language(s)

+
+
+
+ +
+ +
+

Or select one or more programming languages you would like to find repositories for. @@ -63,7 +87,11 @@ export function Hero() { const id = `lang-${language}`; const checked = selected.includes(language); return ( -

- @@ -91,7 +124,10 @@ export function Hero() { const checked = selected.includes(language); return (
  • -
  • - - {/* Search form moved below language selection */} -
    -
    -
    - -
    - -
    + +
    + diff --git a/src/app/(public)/repos/page.tsx b/src/app/(public)/repos/page.tsx index 39e9dc4..9d47ee5 100644 --- a/src/app/(public)/repos/page.tsx +++ b/src/app/(public)/repos/page.tsx @@ -14,10 +14,12 @@ import type { RepoResponse, RepoData, RepoItem, SearchParams } from '@/types'; export default async function ReposPage({ searchParams }: { searchParams: Promise }) { const sp = await searchParams; - const raw = (sp as any).l ?? (sp as any).langs ?? ''; - const langs = (Array.isArray(raw) ? raw : raw.toString().split(',')) - .map((s: string) => decodeURIComponent(s.trim())) - .filter(Boolean); + const raw: string[] = Array.isArray(sp.l) ? sp.l : sp.l ? [String(sp.l)] : []; + + const langs = raw + .flatMap(r => r.split(',')) // handles comma-separated and multi params + .map(s => decodeURIComponent(s.trim())) + .filter(Boolean); const reposRes = await getRepos(langs, sp); if (!reposRes) notFound(); From 89070fe292c7cec946c38f5fd59db9abd9a29602 Mon Sep 17 00:00:00 2001 From: Yash Raj Date: Mon, 13 Oct 2025 21:43:07 +0530 Subject: [PATCH 06/15] Update src/app/(public)/repos/page.tsx Co-authored-by: Usman Sabuwala <51731966+max-programming@users.noreply.github.com> --- src/app/(public)/repos/page.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/app/(public)/repos/page.tsx b/src/app/(public)/repos/page.tsx index 9d47ee5..c5df0af 100644 --- a/src/app/(public)/repos/page.tsx +++ b/src/app/(public)/repos/page.tsx @@ -14,7 +14,7 @@ import type { RepoResponse, RepoData, RepoItem, SearchParams } from '@/types'; export default async function ReposPage({ searchParams }: { searchParams: Promise }) { const sp = await searchParams; - const raw: string[] = Array.isArray(sp.l) ? sp.l : sp.l ? [String(sp.l)] : []; + const langs: string[] = Array.isArray(sp.l) ? sp.l : sp.l ? [String(sp.l)] : []; const langs = raw .flatMap(r => r.split(',')) // handles comma-separated and multi params From 7917ddf7ce6c5d94c745b57862779444235f8539 Mon Sep 17 00:00:00 2001 From: Yash Raj Date: Tue, 14 Oct 2025 17:03:04 +0530 Subject: [PATCH 07/15] Refactor hero search and add repos loading skeleton Refactored the hero search form to separate the input and submit button, improving accessibility and layout. Added a loading skeleton for the repos page to enhance user experience during data fetching. Also simplified language parameter parsing in the repos page. - the repos/loading.tsx is exact same as the /repos/[language]/loading.tsx --- src/app/(public)/_components/hero.tsx | 17 ++-- src/app/(public)/repos/loading.tsx | 109 ++++++++++++++++++++++++++ src/app/(public)/repos/page.tsx | 7 +- 3 files changed, 118 insertions(+), 15 deletions(-) create mode 100644 src/app/(public)/repos/loading.tsx diff --git a/src/app/(public)/_components/hero.tsx b/src/app/(public)/_components/hero.tsx index 2c17261..efc4d09 100644 --- a/src/app/(public)/_components/hero.tsx +++ b/src/app/(public)/_components/hero.tsx @@ -56,6 +56,7 @@ router.push(`/repos?${params.toString()}`); Search your language(s)
    @@ -69,12 +70,6 @@ router.push(`/repos?${params.toString()}`); name="search" /> -

    @@ -142,14 +137,18 @@ router.push(`/repos?${params.toString()}`); })} -

    +
    - +
    diff --git a/src/app/(public)/repos/loading.tsx b/src/app/(public)/repos/loading.tsx new file mode 100644 index 0000000..7206bf3 --- /dev/null +++ b/src/app/(public)/repos/loading.tsx @@ -0,0 +1,109 @@ +export default function Loading() { + return ( + <> + {/* Header Skeleton */} +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + +
    +
    + {/* Title Section Skeleton */} +
    +
    +
    +
    +
    +
    +
    +
    + + {/* Sorter Skeleton */} +
    +
    +
    +
    +
    +
    +
    + + {/* Stars Filter Skeleton */} +
    +
    +
    +
    +
    +
    +
    + + {/* Repository Cards Grid Skeleton */} +
    + {Array.from({ length: 21 }).map((_, index) => ( +
    + {/* Repository Title */} +
    +
    +
    +
    +
    + + {/* Language and Stats */} +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + + {/* Topics */} +
    +
    +
    +
    +
    + + {/* Action Button */} +
    +
    + ))} +
    +
    + + {/* Pagination Skeleton */} +
    +
    +
    + {Array.from({ length: 5 }).map((_, index) => ( +
    + ))} +
    +
    +
    +
    + + ); +} diff --git a/src/app/(public)/repos/page.tsx b/src/app/(public)/repos/page.tsx index 9d47ee5..359560d 100644 --- a/src/app/(public)/repos/page.tsx +++ b/src/app/(public)/repos/page.tsx @@ -14,12 +14,7 @@ import type { RepoResponse, RepoData, RepoItem, SearchParams } from '@/types'; export default async function ReposPage({ searchParams }: { searchParams: Promise }) { const sp = await searchParams; - const raw: string[] = Array.isArray(sp.l) ? sp.l : sp.l ? [String(sp.l)] : []; - - const langs = raw - .flatMap(r => r.split(',')) // handles comma-separated and multi params - .map(s => decodeURIComponent(s.trim())) - .filter(Boolean); + const langs: string[] = Array.isArray(sp.l) ? sp.l : sp.l ? [String(sp.l)] : []; const reposRes = await getRepos(langs, sp); if (!reposRes) notFound(); From 6eff95957e64bb68b1308078623ec5d085ba68af Mon Sep 17 00:00:00 2001 From: "Usman S." Date: Wed, 15 Oct 2025 06:37:36 +0000 Subject: [PATCH 08/15] Format code --- src/app/(public)/_components/hero.tsx | 25 +++++++++++++------------ 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/src/app/(public)/_components/hero.tsx b/src/app/(public)/_components/hero.tsx index efc4d09..79b9132 100644 --- a/src/app/(public)/_components/hero.tsx +++ b/src/app/(public)/_components/hero.tsx @@ -21,7 +21,9 @@ export function Hero() { const toggleLanguage = (language: string) => { setSelected(prev => - prev.includes(language) ? prev.filter(l => l !== language) : [...prev, language] + prev.includes(language) + ? prev.filter(l => l !== language) + : [...prev, language] ); }; @@ -33,19 +35,19 @@ export function Hero() { let chosenLanguages = selected; // Fallback: if no checkbox selected, use the single input value - if (chosenLanguages.length === 0) { - const typed = String(formData.get('search') || '').trim(); - if (typed) { - chosenLanguages = [typed]; - } -} + if (chosenLanguages.length === 0) { + const typed = String(formData.get('search') || '').trim(); + if (typed) { + chosenLanguages = [typed]; + } + } -if (chosenLanguages.length === 0) return; // nothing to search + if (chosenLanguages.length === 0) return; // nothing to search -const params = new URLSearchParams(); -chosenLanguages.forEach(lang => params.append('l', lang.toLowerCase())); + const params = new URLSearchParams(); + chosenLanguages.forEach(lang => params.append('l', lang.toLowerCase())); -router.push(`/repos?${params.toString()}`); + router.push(`/repos?${params.toString()}`); } return ( @@ -149,7 +151,6 @@ router.push(`/repos?${params.toString()}`); Search - From 73b9b9d2970243fa358cfbcb9b3769f73932dafd Mon Sep 17 00:00:00 2001 From: "Usman S." Date: Wed, 15 Oct 2025 06:38:32 +0000 Subject: [PATCH 09/15] Fix styling issues --- src/app/(public)/_components/hero.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/app/(public)/_components/hero.tsx b/src/app/(public)/_components/hero.tsx index 79b9132..3bace8c 100644 --- a/src/app/(public)/_components/hero.tsx +++ b/src/app/(public)/_components/hero.tsx @@ -67,7 +67,7 @@ export function Hero() { @@ -145,7 +145,7 @@ export function Hero() { form="hero-search-form" aria-label="Search repositories" title="Search" - className="bg-transparent rounded-tl-none rounded-bl-none group btn text-hacktoberfest-light border-hacktoberfest-light hover:!border-hacktoberfest-light hover:bg-primary-btn-hover-gradient flex items-center gap-2 px-5" + className="bg-transparent group btn text-hacktoberfest-light border-hacktoberfest-light hover:!border-hacktoberfest-light hover:bg-primary-btn-hover-gradient flex items-center gap-2 px-5" > Search From ba450f6df780d79e9351f11600bd06a6d5a190c2 Mon Sep 17 00:00:00 2001 From: "Usman S." Date: Wed, 15 Oct 2025 06:43:34 +0000 Subject: [PATCH 10/15] Some visual changes in text display --- src/app/(public)/repos/[language]/page.tsx | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/app/(public)/repos/[language]/page.tsx b/src/app/(public)/repos/[language]/page.tsx index d5c0bbb..186eb78 100644 --- a/src/app/(public)/repos/[language]/page.tsx +++ b/src/app/(public)/repos/[language]/page.tsx @@ -31,6 +31,7 @@ export default async function ReposPage({ if (!reposRes) notFound(); const { repos, page } = reposRes; + const languageName = capitalize(decodeURIComponent(language)); return ( <> @@ -43,13 +44,11 @@ export default async function ReposPage({

    - {repos.total_count} + {Intl.NumberFormat().format(repos.total_count)} {' '} repositories for{' '} - {sp.q - ? sp.q + ' in ' + capitalize(decodeURIComponent(language)) - : capitalize(decodeURIComponent(language))} + {sp.q ? sp.q + ' in ' + languageName : languageName}

    From bad6e9eca1fa29bc5377e4e8e2460e373053dccb Mon Sep 17 00:00:00 2001 From: "Usman S." Date: Wed, 15 Oct 2025 06:43:45 +0000 Subject: [PATCH 11/15] Show selected language names in the list --- src/app/(public)/repos/page.tsx | 29 +++++++++++++++++++++++++++-- 1 file changed, 27 insertions(+), 2 deletions(-) diff --git a/src/app/(public)/repos/page.tsx b/src/app/(public)/repos/page.tsx index 359560d..77ee78b 100644 --- a/src/app/(public)/repos/page.tsx +++ b/src/app/(public)/repos/page.tsx @@ -11,15 +11,27 @@ import { db } from '@/lib/db/connection'; import { accountsTable, reportsTable } from '@/lib/db/migrations/schema'; import { eq } from 'drizzle-orm'; import type { RepoResponse, RepoData, RepoItem, SearchParams } from '@/types'; +import { capitalize } from '@/lib/utils'; -export default async function ReposPage({ searchParams }: { searchParams: Promise }) { +export default async function ReposPage({ + searchParams +}: { + searchParams: Promise; +}) { const sp = await searchParams; - const langs: string[] = Array.isArray(sp.l) ? sp.l : sp.l ? [String(sp.l)] : []; + const langs: string[] = Array.isArray(sp.l) + ? sp.l + : sp.l + ? [String(sp.l)] + : []; const reposRes = await getRepos(langs, sp); if (!reposRes) notFound(); const { repos, page } = reposRes; + const languagesList = langs + .map(lang => capitalize(decodeURIComponent(lang))) + .join(', '); return ( <> @@ -28,6 +40,19 @@ export default async function ReposPage({ searchParams }: { searchParams: Promis
    +
    +
    +

    + + {Intl.NumberFormat().format(repos.total_count)} + {' '} + repositories for{' '} + + {sp.q ? sp.q + ' in ' + languagesList : languagesList} + +

    +
    +
    From 02a497ca0bf37eed41dca97663086875c9fa9fb5 Mon Sep 17 00:00:00 2001 From: "Usman S." Date: Wed, 15 Oct 2025 07:04:00 +0000 Subject: [PATCH 12/15] Enable typedRoutes and update Next.js --- next-env.d.ts | 1 + next.config.mjs | 1 + package.json | 4 +- pnpm-lock.yaml | 124 +++++++++---------- src/app/(public)/_components/hero.tsx | 4 +- src/app/(public)/_components/search-form.tsx | 10 +- 6 files changed, 73 insertions(+), 71 deletions(-) diff --git a/next-env.d.ts b/next-env.d.ts index 1b3be08..830fb59 100644 --- a/next-env.d.ts +++ b/next-env.d.ts @@ -1,5 +1,6 @@ /// /// +/// // NOTE: This file should not be edited // see https://nextjs.org/docs/app/api-reference/config/typescript for more information. diff --git a/next.config.mjs b/next.config.mjs index b7213fb..d43ab70 100644 --- a/next.config.mjs +++ b/next.config.mjs @@ -8,6 +8,7 @@ const __dirname = path.dirname(__filename); /**@type {import('next').NextConfig}*/ const config = { reactStrictMode: true, + typedRoutes: true, experimental: { viewTransition: true }, diff --git a/package.json b/package.json index a899d1f..fd1c3f4 100644 --- a/package.json +++ b/package.json @@ -29,11 +29,11 @@ "dotenv-cli": "^10.0.0", "drizzle-orm": "^0.44.6", "eslint": "^9.32.0", - "eslint-config-next": "^15.4.5", + "eslint-config-next": "^15.5.4", "framer-motion": "^12.23.12", "jotai": "^2.12.5", "lucide-react": "^0.536.0", - "next": "^15.4.5", + "next": "^15.5.4", "next-auth": "5.0.0-beta.29", "next-cloudinary": "^6.16.0", "next-seo": "^6.8.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 6d6cd0a..ea436d6 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -66,8 +66,8 @@ importers: specifier: ^9.32.0 version: 9.32.0(jiti@1.21.7) eslint-config-next: - specifier: ^15.4.5 - version: 15.4.5(eslint@9.32.0(jiti@1.21.7))(typescript@5.9.2) + specifier: ^15.5.4 + version: 15.5.4(eslint@9.32.0(jiti@1.21.7))(typescript@5.9.2) framer-motion: specifier: ^12.23.12 version: 12.23.12(react-dom@19.1.1(react@19.1.1))(react@19.1.1) @@ -78,20 +78,20 @@ importers: specifier: ^0.536.0 version: 0.536.0(react@19.1.1) next: - specifier: ^15.4.5 - version: 15.4.5(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + specifier: ^15.5.4 + version: 15.5.4(react-dom@19.1.1(react@19.1.1))(react@19.1.1) next-auth: specifier: 5.0.0-beta.29 - version: 5.0.0-beta.29(next@15.4.5(react-dom@19.1.1(react@19.1.1))(react@19.1.1))(react@19.1.1) + version: 5.0.0-beta.29(next@15.5.4(react-dom@19.1.1(react@19.1.1))(react@19.1.1))(react@19.1.1) next-cloudinary: specifier: ^6.16.0 - version: 6.16.0(next@15.4.5(react-dom@19.1.1(react@19.1.1))(react@19.1.1))(react@19.1.1) + version: 6.16.0(next@15.5.4(react-dom@19.1.1(react@19.1.1))(react@19.1.1))(react@19.1.1) next-seo: specifier: ^6.8.0 - version: 6.8.0(next@15.4.5(react-dom@19.1.1(react@19.1.1))(react@19.1.1))(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + version: 6.8.0(next@15.5.4(react-dom@19.1.1(react@19.1.1))(react@19.1.1))(react-dom@19.1.1(react@19.1.1))(react@19.1.1) nextjs-progressbar: specifier: ^0.0.16 - version: 0.0.16(next@15.4.5(react-dom@19.1.1(react@19.1.1))(react@19.1.1))(react@19.1.1) + version: 0.0.16(next@15.5.4(react-dom@19.1.1(react@19.1.1))(react@19.1.1))(react@19.1.1) postcss: specifier: ^8.5.6 version: 8.5.6 @@ -763,56 +763,56 @@ packages: '@neon-rs/load@0.0.4': resolution: {integrity: sha512-kTPhdZyTQxB+2wpiRcFWrDcejc4JI6tkPuS7UZCG4l6Zvc5kU/gGQ/ozvHTh1XR5tS+UlfAfGuPajjzQjCiHCw==} - '@next/env@15.4.5': - resolution: {integrity: sha512-ruM+q2SCOVCepUiERoxOmZY9ZVoecR3gcXNwCYZRvQQWRjhOiPJGmQ2fAiLR6YKWXcSAh7G79KEFxN3rwhs4LQ==} + '@next/env@15.5.4': + resolution: {integrity: sha512-27SQhYp5QryzIT5uO8hq99C69eLQ7qkzkDPsk3N+GuS2XgOgoYEeOav7Pf8Tn4drECOVDsDg8oj+/DVy8qQL2A==} - '@next/eslint-plugin-next@15.4.5': - resolution: {integrity: sha512-YhbrlbEt0m4jJnXHMY/cCUDBAWgd5SaTa5mJjzOt82QwflAFfW/h3+COp2TfVSzhmscIZ5sg2WXt3MLziqCSCw==} + '@next/eslint-plugin-next@15.5.4': + resolution: {integrity: sha512-SR1vhXNNg16T4zffhJ4TS7Xn7eq4NfKfcOsRwea7RIAHrjRpI9ALYbamqIJqkAhowLlERffiwk0FMvTLNdnVtw==} - '@next/swc-darwin-arm64@15.4.5': - resolution: {integrity: sha512-84dAN4fkfdC7nX6udDLz9GzQlMUwEMKD7zsseXrl7FTeIItF8vpk1lhLEnsotiiDt+QFu3O1FVWnqwcRD2U3KA==} + '@next/swc-darwin-arm64@15.5.4': + resolution: {integrity: sha512-nopqz+Ov6uvorej8ndRX6HlxCYWCO3AHLfKK2TYvxoSB2scETOcfm/HSS3piPqc3A+MUgyHoqE6je4wnkjfrOA==} engines: {node: '>= 10'} cpu: [arm64] os: [darwin] - '@next/swc-darwin-x64@15.4.5': - resolution: {integrity: sha512-CL6mfGsKuFSyQjx36p2ftwMNSb8PQog8y0HO/ONLdQqDql7x3aJb/wB+LA651r4we2pp/Ck+qoRVUeZZEvSurA==} + '@next/swc-darwin-x64@15.5.4': + resolution: {integrity: sha512-QOTCFq8b09ghfjRJKfb68kU9k2K+2wsC4A67psOiMn849K9ZXgCSRQr0oVHfmKnoqCbEmQWG1f2h1T2vtJJ9mA==} engines: {node: '>= 10'} cpu: [x64] os: [darwin] - '@next/swc-linux-arm64-gnu@15.4.5': - resolution: {integrity: sha512-1hTVd9n6jpM/thnDc5kYHD1OjjWYpUJrJxY4DlEacT7L5SEOXIifIdTye6SQNNn8JDZrcN+n8AWOmeJ8u3KlvQ==} + '@next/swc-linux-arm64-gnu@15.5.4': + resolution: {integrity: sha512-eRD5zkts6jS3VfE/J0Kt1VxdFqTnMc3QgO5lFE5GKN3KDI/uUpSyK3CjQHmfEkYR4wCOl0R0XrsjpxfWEA++XA==} engines: {node: '>= 10'} cpu: [arm64] os: [linux] - '@next/swc-linux-arm64-musl@15.4.5': - resolution: {integrity: sha512-4W+D/nw3RpIwGrqpFi7greZ0hjrCaioGErI7XHgkcTeWdZd146NNu1s4HnaHonLeNTguKnL2Urqvj28UJj6Gqw==} + '@next/swc-linux-arm64-musl@15.5.4': + resolution: {integrity: sha512-TOK7iTxmXFc45UrtKqWdZ1shfxuL4tnVAOuuJK4S88rX3oyVV4ZkLjtMT85wQkfBrOOvU55aLty+MV8xmcJR8A==} engines: {node: '>= 10'} cpu: [arm64] os: [linux] - '@next/swc-linux-x64-gnu@15.4.5': - resolution: {integrity: sha512-N6Mgdxe/Cn2K1yMHge6pclffkxzbSGOydXVKYOjYqQXZYjLCfN/CuFkaYDeDHY2VBwSHyM2fUjYBiQCIlxIKDA==} + '@next/swc-linux-x64-gnu@15.5.4': + resolution: {integrity: sha512-7HKolaj+481FSW/5lL0BcTkA4Ueam9SPYWyN/ib/WGAFZf0DGAN8frNpNZYFHtM4ZstrHZS3LY3vrwlIQfsiMA==} engines: {node: '>= 10'} cpu: [x64] os: [linux] - '@next/swc-linux-x64-musl@15.4.5': - resolution: {integrity: sha512-YZ3bNDrS8v5KiqgWE0xZQgtXgCTUacgFtnEgI4ccotAASwSvcMPDLua7BWLuTfucoRv6mPidXkITJLd8IdJplQ==} + '@next/swc-linux-x64-musl@15.5.4': + resolution: {integrity: sha512-nlQQ6nfgN0nCO/KuyEUwwOdwQIGjOs4WNMjEUtpIQJPR2NUfmGpW2wkJln1d4nJ7oUzd1g4GivH5GoEPBgfsdw==} engines: {node: '>= 10'} cpu: [x64] os: [linux] - '@next/swc-win32-arm64-msvc@15.4.5': - resolution: {integrity: sha512-9Wr4t9GkZmMNcTVvSloFtjzbH4vtT4a8+UHqDoVnxA5QyfWe6c5flTH1BIWPGNWSUlofc8dVJAE7j84FQgskvQ==} + '@next/swc-win32-arm64-msvc@15.5.4': + resolution: {integrity: sha512-PcR2bN7FlM32XM6eumklmyWLLbu2vs+D7nJX8OAIoWy69Kef8mfiN4e8TUv2KohprwifdpFKPzIP1njuCjD0YA==} engines: {node: '>= 10'} cpu: [arm64] os: [win32] - '@next/swc-win32-x64-msvc@15.4.5': - resolution: {integrity: sha512-voWk7XtGvlsP+w8VBz7lqp8Y+dYw/MTI4KeS0gTVtfdhdJ5QwhXLmNrndFOin/MDoCvUaLWMkYKATaCoUkt2/A==} + '@next/swc-win32-x64-msvc@15.5.4': + resolution: {integrity: sha512-1ur2tSHZj8Px/KMAthmuI9FMp/YFusMMGoRNJaRZMOlSkgvLjzosSdQI0cJAKogdHl3qXUQKL9MGaYvKwA7DXg==} engines: {node: '>= 10'} cpu: [x64] os: [win32] @@ -1548,8 +1548,8 @@ packages: resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==} engines: {node: '>=10'} - eslint-config-next@15.4.5: - resolution: {integrity: sha512-IMijiXaZ43qFB+Gcpnb374ipTKD8JIyVNR+6VsifFQ/LHyx+A9wgcgSIhCX5PYSjwOoSYD5LtNHKlM5uc23eww==} + eslint-config-next@15.5.4: + resolution: {integrity: sha512-BzgVVuT3kfJes8i2GHenC1SRJ+W3BTML11lAOYFOOPzrk2xp66jBOAGEFRw+3LkYCln5UzvFsLhojrshb5Zfaw==} peerDependencies: eslint: ^7.23.0 || ^8.0.0 || ^9.0.0 typescript: '>=3.3.1' @@ -2179,8 +2179,8 @@ packages: react: '>=16.0.0' react-dom: '>=16.0.0' - next@15.4.5: - resolution: {integrity: sha512-nJ4v+IO9CPmbmcvsPebIoX3Q+S7f6Fu08/dEWu0Ttfa+wVwQRh9epcmsyCPjmL2b8MxC+CkBR97jgDhUUztI3g==} + next@15.5.4: + resolution: {integrity: sha512-xH4Yjhb82sFYQfY3vbkJfgSDgXvBB6a8xPs9i35k6oZJRoQRihZH+4s9Yo2qsWpzBmZ3lPXaJ2KPXLfkvW4LnA==} engines: {node: ^18.18.0 || ^19.8.0 || >= 20.0.0} hasBin: true peerDependencies: @@ -3320,34 +3320,34 @@ snapshots: '@neon-rs/load@0.0.4': optional: true - '@next/env@15.4.5': {} + '@next/env@15.5.4': {} - '@next/eslint-plugin-next@15.4.5': + '@next/eslint-plugin-next@15.5.4': dependencies: fast-glob: 3.3.1 - '@next/swc-darwin-arm64@15.4.5': + '@next/swc-darwin-arm64@15.5.4': optional: true - '@next/swc-darwin-x64@15.4.5': + '@next/swc-darwin-x64@15.5.4': optional: true - '@next/swc-linux-arm64-gnu@15.4.5': + '@next/swc-linux-arm64-gnu@15.5.4': optional: true - '@next/swc-linux-arm64-musl@15.4.5': + '@next/swc-linux-arm64-musl@15.5.4': optional: true - '@next/swc-linux-x64-gnu@15.4.5': + '@next/swc-linux-x64-gnu@15.5.4': optional: true - '@next/swc-linux-x64-musl@15.4.5': + '@next/swc-linux-x64-musl@15.5.4': optional: true - '@next/swc-win32-arm64-msvc@15.4.5': + '@next/swc-win32-arm64-msvc@15.5.4': optional: true - '@next/swc-win32-x64-msvc@15.4.5': + '@next/swc-win32-x64-msvc@15.5.4': optional: true '@nodelib/fs.scandir@2.1.5': @@ -4109,9 +4109,9 @@ snapshots: escape-string-regexp@4.0.0: {} - eslint-config-next@15.4.5(eslint@9.32.0(jiti@1.21.7))(typescript@5.9.2): + eslint-config-next@15.5.4(eslint@9.32.0(jiti@1.21.7))(typescript@5.9.2): dependencies: - '@next/eslint-plugin-next': 15.4.5 + '@next/eslint-plugin-next': 15.5.4 '@rushstack/eslint-patch': 1.12.0 '@typescript-eslint/eslint-plugin': 8.38.0(@typescript-eslint/parser@8.38.0(eslint@9.32.0(jiti@1.21.7))(typescript@5.9.2))(eslint@9.32.0(jiti@1.21.7))(typescript@5.9.2) '@typescript-eslint/parser': 8.38.0(eslint@9.32.0(jiti@1.21.7))(typescript@5.9.2) @@ -4789,29 +4789,29 @@ snapshots: natural-compare@1.4.0: {} - next-auth@5.0.0-beta.29(next@15.4.5(react-dom@19.1.1(react@19.1.1))(react@19.1.1))(react@19.1.1): + next-auth@5.0.0-beta.29(next@15.5.4(react-dom@19.1.1(react@19.1.1))(react@19.1.1))(react@19.1.1): dependencies: '@auth/core': 0.40.0 - next: 15.4.5(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + next: 15.5.4(react-dom@19.1.1(react@19.1.1))(react@19.1.1) react: 19.1.1 - next-cloudinary@6.16.0(next@15.4.5(react-dom@19.1.1(react@19.1.1))(react@19.1.1))(react@19.1.1): + next-cloudinary@6.16.0(next@15.5.4(react-dom@19.1.1(react@19.1.1))(react@19.1.1))(react@19.1.1): dependencies: '@cloudinary-util/types': 1.5.10 '@cloudinary-util/url-loader': 5.10.4 '@cloudinary-util/util': 4.0.0 - next: 15.4.5(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + next: 15.5.4(react-dom@19.1.1(react@19.1.1))(react@19.1.1) react: 19.1.1 - next-seo@6.8.0(next@15.4.5(react-dom@19.1.1(react@19.1.1))(react@19.1.1))(react-dom@19.1.1(react@19.1.1))(react@19.1.1): + next-seo@6.8.0(next@15.5.4(react-dom@19.1.1(react@19.1.1))(react@19.1.1))(react-dom@19.1.1(react@19.1.1))(react@19.1.1): dependencies: - next: 15.4.5(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + next: 15.5.4(react-dom@19.1.1(react@19.1.1))(react@19.1.1) react: 19.1.1 react-dom: 19.1.1(react@19.1.1) - next@15.4.5(react-dom@19.1.1(react@19.1.1))(react@19.1.1): + next@15.5.4(react-dom@19.1.1(react@19.1.1))(react@19.1.1): dependencies: - '@next/env': 15.4.5 + '@next/env': 15.5.4 '@swc/helpers': 0.5.15 caniuse-lite: 1.0.30001731 postcss: 8.4.31 @@ -4819,23 +4819,23 @@ snapshots: react-dom: 19.1.1(react@19.1.1) styled-jsx: 5.1.6(react@19.1.1) optionalDependencies: - '@next/swc-darwin-arm64': 15.4.5 - '@next/swc-darwin-x64': 15.4.5 - '@next/swc-linux-arm64-gnu': 15.4.5 - '@next/swc-linux-arm64-musl': 15.4.5 - '@next/swc-linux-x64-gnu': 15.4.5 - '@next/swc-linux-x64-musl': 15.4.5 - '@next/swc-win32-arm64-msvc': 15.4.5 - '@next/swc-win32-x64-msvc': 15.4.5 + '@next/swc-darwin-arm64': 15.5.4 + '@next/swc-darwin-x64': 15.5.4 + '@next/swc-linux-arm64-gnu': 15.5.4 + '@next/swc-linux-arm64-musl': 15.5.4 + '@next/swc-linux-x64-gnu': 15.5.4 + '@next/swc-linux-x64-musl': 15.5.4 + '@next/swc-win32-arm64-msvc': 15.5.4 + '@next/swc-win32-x64-msvc': 15.5.4 sharp: 0.34.3 transitivePeerDependencies: - '@babel/core' - babel-plugin-macros - nextjs-progressbar@0.0.16(next@15.4.5(react-dom@19.1.1(react@19.1.1))(react@19.1.1))(react@19.1.1): + nextjs-progressbar@0.0.16(next@15.5.4(react-dom@19.1.1(react@19.1.1))(react@19.1.1))(react@19.1.1): dependencies: '@types/nprogress': 0.2.3 - next: 15.4.5(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + next: 15.5.4(react-dom@19.1.1(react@19.1.1))(react@19.1.1) nprogress: 0.2.0 prop-types: 15.8.1 react: 19.1.1 diff --git a/src/app/(public)/_components/hero.tsx b/src/app/(public)/_components/hero.tsx index 3bace8c..f8fd965 100644 --- a/src/app/(public)/_components/hero.tsx +++ b/src/app/(public)/_components/hero.tsx @@ -32,13 +32,13 @@ export function Hero() { function handleSearch(e: React.FormEvent) { e.preventDefault(); const formData = new FormData(e.target as HTMLFormElement); - let chosenLanguages = selected; + const chosenLanguages = [...selected]; // Fallback: if no checkbox selected, use the single input value if (chosenLanguages.length === 0) { const typed = String(formData.get('search') || '').trim(); if (typed) { - chosenLanguages = [typed]; + chosenLanguages.push(typed); } } diff --git a/src/app/(public)/_components/search-form.tsx b/src/app/(public)/_components/search-form.tsx index 9ceacc4..cb52e32 100644 --- a/src/app/(public)/_components/search-form.tsx +++ b/src/app/(public)/_components/search-form.tsx @@ -26,20 +26,20 @@ export function SearchForm() { } function onSubmit({ searchQuery }: FormValues) { + if (!pathname.startsWith('/repos')) return; + + const reposPathname = pathname as `/repos/${string}`; const trimmedQuery = searchQuery.trim(); if (trimmedQuery !== '') { const sp = new URLSearchParams(searchParams); sp.set('q', trimmedQuery); - router.push(`${pathname}?${sp.toString()}`); + router.push(`${reposPathname}?${sp.toString()}`); } } return (
    -
    +
    Date: Wed, 15 Oct 2025 07:06:36 +0000 Subject: [PATCH 13/15] Fix type error --- src/app/(public)/repos/_components/sorter.tsx | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/app/(public)/repos/_components/sorter.tsx b/src/app/(public)/repos/_components/sorter.tsx index 5afa2fe..545c35c 100644 --- a/src/app/(public)/repos/_components/sorter.tsx +++ b/src/app/(public)/repos/_components/sorter.tsx @@ -20,9 +20,11 @@ enum SortTypes { LeastRecentlyUpdated = 'Least recently updated' } +type Pathname = '/repos' | `/repos/${string}`; + export function Sorter() { const searchParams = useSearchParams(); - const pathname = usePathname(); + const pathname = usePathname() as Pathname; const navigationItems = [ { From 5f1bbd32486c687da2e381384759cbde8580baef Mon Sep 17 00:00:00 2001 From: "Usman S." Date: Wed, 15 Oct 2025 07:09:30 +0000 Subject: [PATCH 14/15] fix type error --- src/app/(public)/repos/_components/stars-filter.tsx | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/app/(public)/repos/_components/stars-filter.tsx b/src/app/(public)/repos/_components/stars-filter.tsx index 0075b7e..d2fcc1d 100644 --- a/src/app/(public)/repos/_components/stars-filter.tsx +++ b/src/app/(public)/repos/_components/stars-filter.tsx @@ -14,9 +14,11 @@ interface FormValues { endStars: number | ''; } +type Pathname = '/repos' | `/repos/${string}`; + export function StarsFilter() { const router = useRouter(); - const pathname = usePathname(); + const pathname = usePathname() as Pathname; const searchParams = useSearchParams(); const params = useParams(); const { handleSubmit, control, reset } = useForm({ @@ -95,4 +97,4 @@ export function StarsFilter() {
    ); -} \ No newline at end of file +} From 218ae78801f059e9f2d40f97877599c66179f146 Mon Sep 17 00:00:00 2001 From: "Usman S." Date: Wed, 15 Oct 2025 07:12:10 +0000 Subject: [PATCH 15/15] Remove default sort --- src/app/(public)/repos/page.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/app/(public)/repos/page.tsx b/src/app/(public)/repos/page.tsx index 77ee78b..3b03988 100644 --- a/src/app/(public)/repos/page.tsx +++ b/src/app/(public)/repos/page.tsx @@ -79,7 +79,7 @@ async function getRepos( const session = await auth(); const { p: page = '1', - s: sort = 'updated', + s: sort = '', o: order = 'desc', q: searchQuery = '', startStars = '1',