Skip to content

Commit 7a276ec

Browse files
committed
fix: as per req
fixed the requested changes - added search button below - directory move from `/(public)/repos/[language]/_components` to `/(public)/repos/_components`
1 parent 3f0f462 commit 7a276ec

File tree

9 files changed

+186
-41
lines changed

9 files changed

+186
-41
lines changed

src/app/(public)/_components/hero.tsx

Lines changed: 27 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -51,32 +51,10 @@ export function Hero() {
5151
<div className="z-50 flex flex-col space-y-8 justify-center items-center text-center min-h-screen pt-28 sm:pt-24">
5252
<div className="max-w-md space-y-5 px-4">
5353
<h1 className="text-3xl sm:text-4xl md:text-5xl font-medium uppercase heading-text">
54-
Search your language
54+
Search your language(s)
5555
</h1>
56-
<form
57-
className="items-center w-full max-w-xs mx-auto form-control outline-none"
58-
onSubmit={handleSearch}
59-
>
60-
<div className="flex w-full">
61-
<div className="relative flex w-full">
62-
<input
63-
type="text"
64-
placeholder="Type a language (optional)"
65-
className="w-full max-w-xs bg-transparent rounded-tr-none rounded-br-none input input-bordered text-hacktoberfest-light border-hacktoberfest-light
66-
focus:border-hacktoberfest-light focus:!outline-none focus-visible:!outline-none placeholder:text-hacktoberfest-light text-sm sm:text-base"
67-
name="search"
68-
/>
69-
</div>
70-
<button
71-
type="submit"
72-
className="bg-transparent rounded-tl-none rounded-bl-none group btn btn-square text-hacktoberfest-light border-hacktoberfest-light hover:!border-hacktoberfest-light hover:bg-primary-btn-hover-gradient flex-shrink-0"
73-
>
74-
<Search size={16} className="sm:w-5 sm:h-5" />
75-
</button>
76-
</div>
77-
</form>
7856
<p className="font-medium uppercase text-hacktoberfest-light text-sm sm:text-base">
79-
Or select the programming languages you would like to find
57+
Or select one or more programming languages you would like to find
8058
repositories for.
8159
</p>
8260

@@ -99,7 +77,7 @@ export function Hero() {
9977
})}
10078
</div>
10179

102-
<div className="dropdown dropdown-top">
80+
<div className="dropdown dropdown-top mt-4">
10381
<Button tabIndex={0} className="umami--click--otherlangs-button text-sm sm:text-base">
10482
Other languages
10583
</Button>
@@ -128,6 +106,30 @@ export function Hero() {
128106
})}
129107
</ul>
130108
</div>
109+
110+
{/* Search form moved below language selection */}
111+
<form
112+
className="items-center w-full max-w-xs mx-auto form-control outline-none mt-8"
113+
onSubmit={handleSearch}
114+
>
115+
<div className="flex w-full">
116+
<div className="relative flex w-full">
117+
<input
118+
type="text"
119+
placeholder="Type a language (optional)"
120+
className="w-full max-w-xs bg-transparent rounded-tr-none rounded-br-none input input-bordered text-hacktoberfest-light border-hacktoberfest-light
121+
focus:border-hacktoberfest-light focus:!outline-none focus-visible:!outline-none placeholder:text-hacktoberfest-light text-sm sm:text-base"
122+
name="search"
123+
/>
124+
</div>
125+
<button
126+
type="submit"
127+
className="bg-transparent rounded-tl-none rounded-bl-none group btn btn-square text-hacktoberfest-light border-hacktoberfest-light hover:!border-hacktoberfest-light hover:bg-primary-btn-hover-gradient flex-shrink-0"
128+
>
129+
<Search size={16} className="sm:w-5 sm:h-5" />
130+
</button>
131+
</div>
132+
</form>
131133
</div>
132134
<MarqueTextAnimation />
133135
</div>

src/app/(public)/repos/[language]/page.tsx

Lines changed: 24 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,11 @@ import { env } from '@/env.mjs';
22
import { notFound } from 'next/navigation';
33
import { capitalize } from '@/lib/utils';
44
import { Header } from '@/app/(public)/_components/header';
5-
import { ScrollToTop } from './_components/scroll-to-top';
6-
import { RepoCard } from './_components/repo-card';
7-
import { Sorter } from './_components/sorter';
8-
import { StarsFilter } from './_components/stars-filter';
9-
import { Pagination } from './_components/pagination';
5+
import { ScrollToTop } from '../_components/scroll-to-top';
6+
import { RepoCard } from '../_components/repo-card';
7+
import { Sorter } from '../_components/sorter';
8+
import { StarsFilter } from '../_components/stars-filter';
9+
import { Pagination } from '../_components/pagination';
1010
import type { RepoData, RepoItem, RepoResponse, SearchParams } from '@/types';
1111
import type { Metadata } from 'next';
1212
import { auth } from '@/auth';
@@ -105,7 +105,16 @@ async function getRepos(
105105
? `stars:<${endStars}`
106106
: '';
107107

108-
const languages = language.split(',').map(l => l.trim()); // split multi-language
108+
const apiUrl = new URL('https://api.github.com/search/repositories');
109+
apiUrl.searchParams.set('page', page.toString());
110+
apiUrl.searchParams.set('per_page', '21');
111+
apiUrl.searchParams.set('sort', sort.toString());
112+
apiUrl.searchParams.set('order', order.toString());
113+
apiUrl.searchParams.set(
114+
'q',
115+
`topic:hacktoberfest language:${language} ${searchQuery} ${starsQuery}`
116+
);
117+
109118
const headers: HeadersInit = {
110119
Accept: 'application/vnd.github.mercy-preview+json'
111120
};
@@ -127,6 +136,10 @@ async function getRepos(
127136
headers.Authorization = `Bearer ${env.AUTH_GITHUB_TOKEN}`;
128137
}
129138

139+
const res = await fetch(apiUrl, { headers });
140+
if (!res.ok) return undefined;
141+
142+
const repos = (await res.json()) as RepoData;
130143
const reports = await getReportedRepos();
131144
let allRepos: RepoItem[] = [];
132145

@@ -153,24 +166,19 @@ async function getRepos(
153166
allRepos = allRepos.concat(filteredItems);
154167
}
155168

156-
if (allRepos.length < 1) return undefined;
169+
repos.items = repos.items.filter((repo: RepoItem) => {
170+
return !repo.archived && !reports.find(report => report.repoId === repo.id);
171+
});
157172

158-
// sort merged repos by updated date descending
159-
allRepos.sort(
160-
(a, b) => new Date(b.updated_at).getTime() - new Date(a.updated_at).getTime()
161-
);
173+
if (!Array.isArray(repos.items) || repos.items?.length < 1) return undefined;
162174

163175
return {
164176
page: +page.toString(),
165177
languageName: language,
166-
repos: {
167-
...{ total_count: allRepos.length, incomplete_results: false },
168-
items: allRepos
169-
}
178+
repos
170179
};
171180
}
172181

173-
174182
async function getReportedRepos() {
175183
const reports = await db
176184
.select()

src/app/(public)/repos/[language]/_components/pagination.tsx renamed to src/app/(public)/repos/_components/pagination.tsx

File renamed without changes.

src/app/(public)/repos/[language]/_components/repo-card.tsx renamed to src/app/(public)/repos/_components/repo-card.tsx

File renamed without changes.

src/app/(public)/repos/[language]/_components/report-button.tsx renamed to src/app/(public)/repos/_components/report-button.tsx

File renamed without changes.

src/app/(public)/repos/[language]/_components/scroll-to-top.tsx renamed to src/app/(public)/repos/_components/scroll-to-top.tsx

File renamed without changes.
File renamed without changes.

src/app/(public)/repos/[language]/_components/stars-filter.tsx renamed to src/app/(public)/repos/_components/stars-filter.tsx

File renamed without changes.

src/app/(public)/repos/page.tsx

Lines changed: 135 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,135 @@
1+
import { env } from '@/env.mjs';
2+
import { notFound } from 'next/navigation';
3+
import { Header } from '@/app/(public)/_components/header';
4+
import { ScrollToTop } from './_components/scroll-to-top';
5+
import { RepoCard } from './_components/repo-card';
6+
import { Sorter } from './_components/sorter';
7+
import { StarsFilter } from './_components/stars-filter';
8+
import { Pagination } from './_components/pagination';
9+
import { auth } from '@/auth';
10+
import { db } from '@/lib/db/connection';
11+
import { accountsTable, reportsTable } from '@/lib/db/migrations/schema';
12+
import { eq } from 'drizzle-orm';
13+
import type { RepoResponse, RepoData, RepoItem, SearchParams } from '@/types';
14+
15+
export default async function ReposPage({ searchParams }: { searchParams: Promise<SearchParams> }) {
16+
const sp = await searchParams;
17+
const raw = (sp as any).l ?? (sp as any).langs ?? '';
18+
const langs = (Array.isArray(raw) ? raw : raw.toString().split(','))
19+
.map((s: string) => decodeURIComponent(s.trim()))
20+
.filter(Boolean);
21+
22+
const reposRes = await getRepos(langs, sp);
23+
if (!reposRes) notFound();
24+
25+
const { repos, page } = reposRes;
26+
27+
return (
28+
<>
29+
<Header />
30+
<ScrollToTop />
31+
<div className="w-full overflow-x-hidden">
32+
<div className="container mx-auto px-4 pt-32 sm:pt-36 md:pt-40 pb-8">
33+
<div className="min-h-screen">
34+
<Sorter />
35+
<StarsFilter />
36+
<div className="grid grid-cols-1 gap-6 px-2 sm:px-4 sm:grid-cols-2 lg:grid-cols-3">
37+
{repos.items.map(repo => (
38+
<RepoCard key={repo.id} repo={repo} />
39+
))}
40+
</div>
41+
</div>
42+
<Pagination
43+
page={page}
44+
totalCount={repos.total_count}
45+
searchParams={sp}
46+
/>
47+
</div>
48+
</div>
49+
</>
50+
);
51+
}
52+
53+
async function getRepos(
54+
languages: string[],
55+
searchParams: SearchParams
56+
): Promise<RepoResponse | undefined> {
57+
const session = await auth();
58+
const {
59+
p: page = '1',
60+
s: sort = 'updated',
61+
o: order = 'desc',
62+
q: searchQuery = '',
63+
startStars = '1',
64+
endStars = ''
65+
} = searchParams;
66+
67+
const starsQuery =
68+
startStars && endStars
69+
? `stars:${startStars}..${endStars}`
70+
: startStars && !endStars
71+
? `stars:>${startStars}`
72+
: !startStars && endStars
73+
? `stars:<${endStars}`
74+
: '';
75+
76+
const combinedLangs = languages.map(l => `language:${l}`).join(' ');
77+
78+
const apiUrl = new URL('https://api.github.com/search/repositories');
79+
apiUrl.searchParams.set('page', page.toString());
80+
apiUrl.searchParams.set('per_page', '21');
81+
apiUrl.searchParams.set('sort', sort.toString());
82+
apiUrl.searchParams.set('order', order.toString());
83+
apiUrl.searchParams.set(
84+
'q',
85+
`topic:hacktoberfest ${combinedLangs} ${searchQuery} ${starsQuery}`
86+
);
87+
88+
const headers: HeadersInit = {
89+
Accept: 'application/vnd.github.mercy-preview+json'
90+
};
91+
92+
const userId = session?.user?.id;
93+
94+
if (userId) {
95+
const [account] = await db
96+
.select()
97+
.from(accountsTable)
98+
.where(eq(accountsTable.userId, userId))
99+
.limit(1);
100+
101+
if (account && account.access_token) {
102+
headers.Authorization = `Bearer ${account.access_token}`;
103+
} else if (env.AUTH_GITHUB_TOKEN) {
104+
headers.Authorization = `Bearer ${env.AUTH_GITHUB_TOKEN}`;
105+
}
106+
} else if (env.AUTH_GITHUB_TOKEN) {
107+
headers.Authorization = `Bearer ${env.AUTH_GITHUB_TOKEN}`;
108+
}
109+
110+
const res = await fetch(apiUrl, { headers });
111+
if (!res.ok) return undefined;
112+
113+
const repos = (await res.json()) as RepoData;
114+
const reports = await getReportedRepos();
115+
116+
repos.items = repos.items.filter((repo: RepoItem) => {
117+
return !repo.archived && !reports.find(report => report.repoId === repo.id);
118+
});
119+
120+
return {
121+
page: +page.toString(),
122+
languageName: languages.join(', '),
123+
repos
124+
};
125+
}
126+
127+
async function getReportedRepos() {
128+
const reports = await db
129+
.select()
130+
.from(reportsTable)
131+
.where(eq(reportsTable.valid, false))
132+
.limit(100);
133+
134+
return reports;
135+
}

0 commit comments

Comments
 (0)