Skip to content

Commit 86ca343

Browse files
samyak003arkid15r
andauthored
Updated user details page (#1176)
* Updated user details page * Added Pull requests to homepage * Update code --------- Co-authored-by: Arkadii Yakovets <[email protected]> Co-authored-by: Arkadii Yakovets <[email protected]>
1 parent 8f16ad9 commit 86ca343

File tree

10 files changed

+157
-16
lines changed

10 files changed

+157
-16
lines changed

backend/apps/github/graphql/nodes/pull_request.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ class PullRequestNode(BaseNode):
1414
class Meta:
1515
model = PullRequest
1616
fields = (
17+
"author",
1718
"created_at",
1819
"title",
1920
)

backend/apps/github/graphql/queries/issue.py

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
"""GraphQL queries for handling GitHub issues."""
22

33
import graphene
4+
from django.db.models import OuterRef, Subquery
45

56
from apps.common.graphql.queries import BaseQuery
67
from apps.github.graphql.nodes.issue import IssueNode
@@ -43,12 +44,15 @@ def resolve_recent_issues(root, info, limit=15, distinct=False, login=None):
4344
queryset = queryset.filter(author__login=login)
4445

4546
if distinct:
46-
queryset = queryset.distinct(
47-
"author_id",
48-
"created_at",
47+
latest_issue_per_author = (
48+
queryset.filter(author_id=OuterRef("author_id"))
49+
.order_by("-created_at")
50+
.values("id")[:1]
51+
)
52+
queryset = queryset.filter(
53+
id__in=Subquery(latest_issue_per_author),
4954
).order_by(
5055
"-created_at",
51-
"author_id",
5256
)
5357

5458
return queryset[:limit]

backend/apps/github/graphql/queries/pull_request.py

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
"""Github pull requests GraphQL queries."""
22

33
import graphene
4+
from django.db.models import OuterRef, Subquery
45

56
from apps.common.graphql.queries import BaseQuery
67
from apps.github.graphql.nodes.pull_request import PullRequestNode
@@ -13,13 +14,31 @@ class PullRequestQuery(BaseQuery):
1314
recent_pull_requests = graphene.List(
1415
PullRequestNode,
1516
limit=graphene.Int(default_value=6),
17+
distinct=graphene.Boolean(default_value=False),
1618
login=graphene.String(required=False),
1719
)
1820

19-
def resolve_recent_pull_requests(root, info, limit, login=None):
21+
def resolve_recent_pull_requests(root, info, limit, distinct=False, login=None):
2022
"""Resolve recent pull requests."""
21-
queryset = PullRequest.objects.select_related("author").order_by("-created_at")
23+
queryset = PullRequest.objects.select_related(
24+
"author",
25+
).order_by(
26+
"-created_at",
27+
)
28+
2229
if login:
2330
queryset = queryset.filter(author__login=login)
2431

32+
if distinct:
33+
latest_pull_request_per_author = (
34+
queryset.filter(author_id=OuterRef("author_id"))
35+
.order_by("-created_at")
36+
.values("id")[:1]
37+
)
38+
queryset = queryset.filter(
39+
id__in=Subquery(latest_pull_request_per_author),
40+
).order_by(
41+
"-created_at",
42+
)
43+
2544
return queryset[:limit]

backend/apps/github/graphql/queries/release.py

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
"""GraphQL queries for handling OWASP releases."""
22

33
import graphene
4+
from django.db.models import OuterRef, Subquery
45

56
from apps.common.graphql.queries import BaseQuery
67
from apps.github.graphql.nodes.release import ReleaseNode
@@ -31,18 +32,22 @@ def resolve_recent_releases(root, info, limit=15, distinct=False):
3132
Queryset containing the filtered list of releases.
3233
3334
"""
34-
query = Release.objects.filter(
35+
queryset = Release.objects.filter(
3536
is_draft=False,
3637
is_pre_release=False,
3738
published_at__isnull=False,
3839
).order_by("-published_at")
3940

4041
if distinct:
41-
query = query.distinct(
42-
"author_id",
43-
"published_at",
42+
latest_release_per_author = (
43+
queryset.filter(author_id=OuterRef("author_id"))
44+
.order_by("-published_at")
45+
.values("id")[:1]
46+
)
47+
queryset = queryset.filter(
48+
id__in=Subquery(latest_release_per_author),
4449
).order_by(
4550
"-published_at",
4651
)
4752

48-
return query[:limit]
53+
return queryset[:limit]

frontend/__tests__/unit/data/mockHomeData.ts

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,26 @@ export const mockGraphQLData = {
7979
url: 'https://nest.owasp.org/events/event-1',
8080
},
8181
],
82+
recentPullRequests: [
83+
{
84+
createdAt: '2025-03-25T10:00:00Z',
85+
title: 'Fix authentication bug',
86+
author: {
87+
name: 'John Doe',
88+
avatarUrl: 'https://avatars.githubusercontent.com/u/58754215?v=4',
89+
},
90+
url: 'https://github.com/example/repo/pull/1',
91+
},
92+
{
93+
createdAt: '2025-03-24T15:30:00Z',
94+
title: 'Add new feature',
95+
author: {
96+
login: 'jane-smith',
97+
avatarUrl: 'https://avatars.githubusercontent.com/u/58754221?v=4',
98+
},
99+
url: 'https://github.com/example/repo/pull/2',
100+
},
101+
],
82102
}
83103

84104
export const mockAlgoliaData = {

frontend/__tests__/unit/pages/Home.test.tsx

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -166,4 +166,18 @@ describe('Home', () => {
166166
})
167167
})
168168
})
169+
170+
test('renders Recent Pull Requests section', async () => {
171+
render(<Home />)
172+
173+
await waitFor(() => {
174+
expect(screen.getByText('Recent Pull Requests')).toBeInTheDocument()
175+
mockGraphQLData.recentPullRequests.forEach((pullRequest) => {
176+
expect(screen.getByText(pullRequest.title)).toBeInTheDocument()
177+
expect(
178+
screen.getByText(pullRequest.author.name || pullRequest.author.login)
179+
).toBeInTheDocument()
180+
})
181+
})
182+
})
169183
})

frontend/src/api/queries/homeQueries.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,16 @@ export const GET_MAIN_PAGE_DATA = gql`
4343
name
4444
}
4545
}
46+
recentPullRequests(limit: 5, distinct: $distinct) {
47+
author {
48+
avatarUrl
49+
login
50+
name
51+
}
52+
createdAt
53+
title
54+
url
55+
}
4656
recentReleases(limit: 5, distinct: $distinct) {
4757
author {
4858
avatarUrl

frontend/src/components/ItemCardList.tsx

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,11 @@ const ItemCardList = ({
1818
commentsCount: number
1919
publishedAt: string
2020
tagName: string
21+
author: {
22+
avatarUrl: string
23+
login: string
24+
name: string
25+
}
2126
}) => JSX.Element
2227
}) => (
2328
<SecondaryCard title={title}>

frontend/src/pages/Home.tsx

Lines changed: 56 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -272,18 +272,69 @@ export default function Home() {
272272
)}
273273
/>
274274
<ItemCardList
275-
title="Recent Releases"
276-
data={data.recentReleases}
275+
title="Recent Pull Requests"
276+
data={data.recentPullRequests}
277277
renderDetails={(item) => (
278278
<div className="mt-2 flex items-center text-sm text-gray-600 dark:text-gray-400">
279279
<FontAwesomeIcon icon={faCalendar} className="mr-2 h-4 w-4" />
280-
<span>{formatDate(item.publishedAt)}</span>
281-
<FontAwesomeIcon icon={faTag} className="ml-4 mr-2 h-4 w-4" />
282-
<span>{item.tagName}</span>
280+
<span>{formatDate(item.createdAt)}</span>
281+
{item?.author.name || item?.author.login ? (
282+
<>
283+
<FontAwesomeIcon icon={faUser} className="ml-4 mr-2 h-4 w-4" />
284+
<span>{item.author.name || item.author.login}</span>
285+
</>
286+
) : null}
283287
</div>
284288
)}
285289
/>
286290
</div>
291+
<SecondaryCard title="Recent Releases">
292+
{data.recentReleases && data.recentReleases.length > 0 ? (
293+
<div className="grid gap-4 sm:grid-cols-1 md:grid-cols-2 lg:grid-cols-3">
294+
{data.recentReleases.map((item, index) => (
295+
<div
296+
key={index}
297+
className="mb-4 w-full rounded-lg bg-gray-200 p-4 dark:bg-gray-700"
298+
>
299+
<div className="flex w-full flex-col justify-between">
300+
<div className="flex w-full items-center">
301+
<a
302+
className="flex-shrink-0 text-blue-400 hover:underline dark:text-blue-200"
303+
href={`/community/users/${item?.author?.login}`}
304+
>
305+
<img
306+
src={item?.author?.avatarUrl}
307+
alt={item?.author?.name}
308+
className="mr-2 h-6 w-6 rounded-full"
309+
/>
310+
</a>
311+
312+
<h3 className="flex-1 overflow-hidden text-ellipsis whitespace-nowrap font-semibold">
313+
<a
314+
className="text-blue-500 hover:underline dark:text-blue-400"
315+
href={item?.url}
316+
target="_blank"
317+
>
318+
{item.name}
319+
</a>
320+
</h3>
321+
</div>
322+
<div className="ml-0.5 w-full">
323+
<div className="mt-2 flex items-center text-sm text-gray-600 dark:text-gray-400">
324+
<FontAwesomeIcon icon={faCalendar} className="mr-2 h-4 w-4" />
325+
<span>{formatDate(item.publishedAt)}</span>
326+
<FontAwesomeIcon icon={faTag} className="ml-4 mr-2 h-4 w-4" />
327+
<span>{item.tagName}</span>
328+
</div>
329+
</div>
330+
</div>
331+
</div>
332+
))}
333+
</div>
334+
) : (
335+
<p>No recent releases.</p>
336+
)}
337+
</SecondaryCard>
287338
<SecondaryCard title="Recent News & Opinions" className="overflow-hidden">
288339
<div className="grid gap-4 sm:grid-cols-1 md:grid-cols-2">
289340
{data.recentPosts.map((post) => (

frontend/src/types/home.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ export type MainPageData = {
77
recentIssues: ProjectIssuesType[]
88
recentReleases: ProjectReleaseType[]
99
upcomingEvents: EventType[]
10+
recentPullRequests: PullRequestsType[]
1011
recentChapters: {
1112
createdAt: string
1213
key: string
@@ -45,3 +46,14 @@ export type SponsorType = {
4546
sponsorType: string
4647
url: string
4748
}
49+
50+
export type PullRequestsType = {
51+
author: {
52+
avatarUrl: string
53+
login: string
54+
name: string
55+
}
56+
createdAt: string
57+
title: string
58+
url: string
59+
}

0 commit comments

Comments
 (0)