Skip to content

Commit 779dfa1

Browse files
committed
improve info to also have review status
1 parent be4ff02 commit 779dfa1

File tree

3 files changed

+223
-106
lines changed

3 files changed

+223
-106
lines changed

src/App.vue

Lines changed: 144 additions & 106 deletions
Original file line numberDiff line numberDiff line change
@@ -6,44 +6,74 @@
66
>
77
<div class="flex flex-col">
88
<div class="flex justify-center text-left">
9-
<div v-if="data" class="flex flex-col gap-4">
9+
<div v-if="pullRequests" class="flex flex-col gap-4">
1010
<div
11-
v-for="pr in data"
11+
v-for="pr in pullRequests"
1212
:key="pr.id"
1313
:class="{
14-
'flex flex-wrap gap-y-0.5': true,
14+
'flex justify-between gap-16': true,
1515
'transition-opacity duration-100 ease-in-out opacity-50 hover:opacity-100':
1616
pr.draft,
1717
}"
1818
>
19-
<div class="flex items-center gap-2 grow-1">
20-
<a
21-
:href="pr.html_url"
22-
target="_blank"
23-
:class="{
24-
'font-bold hover:text-sky-400': true,
25-
'text-muted-color': pr.draft,
26-
}"
27-
>
28-
{{ pr.title }}
29-
</a>
30-
<Tag
31-
v-for="label in pr.labels"
32-
:value="label.name"
33-
:key="`${label.id}`"
34-
v-tooltip="label.description"
35-
:pt:label:class="'text-2xs'"
36-
:dt="{
37-
padding: '0.15rem 0.5rem',
38-
}"
39-
:style="{
40-
backgroundColor: `#${label.color}`,
41-
color: getFrontColor(`#${label.color}`),
42-
}"
43-
></Tag>
19+
<div class="flex flex-col">
20+
<div class="flex items-center gap-2 grow-1">
21+
<a
22+
:href="pr.html_url"
23+
target="_blank"
24+
:class="{
25+
'font-bold hover:text-sky-400': true,
26+
'text-muted-color': pr.draft,
27+
}"
28+
>
29+
{{ pr.title }}
30+
</a>
31+
<Tag
32+
v-for="label in pr.labels"
33+
:value="label.name"
34+
:key="`${label.id}`"
35+
v-tooltip="label.description"
36+
:pt:label:class="'text-2xs'"
37+
:dt="{
38+
padding: '0.15rem 0.5rem',
39+
}"
40+
:style="{
41+
backgroundColor: `#${label.color}`,
42+
color: getFrontColor(`#${label.color}`),
43+
}"
44+
></Tag>
45+
</div>
46+
47+
<div class="text-muted-color text-xs w-full">
48+
<span class="text-muted-color-emphasis">#{{ pr.number }}</span>
49+
opened by <UserTag :user="pr.user" />
50+
<span v-if="pr.base.ref !== 'master'" class="ml-2">
51+
&rArr;
52+
<span class="ml-2 text-primary-emphasis">{{
53+
pr.base.ref
54+
}}</span>
55+
</span>
56+
</div>
57+
</div>
58+
<div
59+
v-if="pr.review && pr.review.changes_requested"
60+
class="text-red-400 font-bold"
61+
>
62+
Changes requested.
63+
</div>
64+
<div v-else-if="pr.review && pr.review.approved" class="flex flex-col">
65+
<div class="text-green-500 text-right">Approved</div>
66+
<div class="text-xs">
67+
by
68+
<UserTag
69+
v-for="(user, idx) in pr.review.by"
70+
:user="user"
71+
:key="`u${pr.id}-${idx}`"
72+
/>
73+
</div>
4474
</div>
4575
<div
46-
v-if="!pr.draft"
76+
v-else-if="!pr.draft"
4777
:class="{
4878
'shrink-1': true,
4979
'text-orange-400': !pr.draft && isMoreThanXdays(pr.created_at, 7),
@@ -58,22 +88,10 @@
5888
<span v-else-if="isMoreThanXdays(pr.created_at, 2)">🙂</span>
5989
<span v-else>😄</span> -->
6090
</div>
61-
<div v-if="pr.draft" class="text-muted-color">
91+
<div v-else-if="pr.draft" class="text-muted-color">
6292
{{ computeHowLongAgo(pr.created_at) }}
6393
<!-- <span>🫣</span> -->
6494
</div>
65-
<div class="text-muted-color text-xs w-full">
66-
<span class="text-muted-color-emphasis">#{{ pr.number }}</span> opened
67-
by
68-
<a :href="pr.user.html_url" class="text-muted-color-emphasis"
69-
><img :src="pr.user.avatar_url" class="rounded-full h-4 inline" />
70-
{{ pr.user.login }}</a
71-
>
72-
<span v-if="pr.base.ref !== 'master'" class="ml-2">
73-
&rArr;
74-
<span class="ml-2 text-primary-emphasis">{{ pr.base.ref }}</span>
75-
</span>
76-
</div>
7795
</div>
7896
</div>
7997
<div v-else>
@@ -90,63 +108,18 @@ import { ref } from 'vue';
90108
import { onMounted } from 'vue';
91109
import Tag from 'primevue/tag';
92110
import { Panel } from 'primevue';
111+
import {
112+
type PullRequest,
113+
type PullRequestReview,
114+
type ReviewStatus,
115+
type User,
116+
APPROVED,
117+
CHANGES_REQUESTED,
118+
CONTRIBUTOR,
119+
} from './ResponsesTypes.ts';
120+
import UserTag from './components/UserTag.vue';
93121
94-
type PullRequest = {
95-
active_lock_reason: unknown;
96-
assignee: unknown;
97-
assignees: string[];
98-
author_association: string;
99-
auto_merge: null;
100-
body: string;
101-
closed_at: null;
102-
comments_url: string;
103-
commits_url: string;
104-
created_at: string;
105-
diff_url: string;
106-
draft: boolean;
107-
html_url: string;
108-
id: number;
109-
issue_url: string;
110-
labels: { color: string; id: number; description: string; name: string }[];
111-
locked: boolean;
112-
merge_commit_sha: string;
113-
merged_at: null;
114-
milestone: null;
115-
node_id: string;
116-
number: number;
117-
patch_url: string;
118-
requested_reviewers: [];
119-
review_comment_url: string;
120-
review_comments_url: string;
121-
state: 'open';
122-
statuses_url: string;
123-
title: string;
124-
updated_at: string;
125-
url: string;
126-
user: {
127-
login: string;
128-
id: number;
129-
node_id: string;
130-
avatar_url: string;
131-
gravatar_id: string;
132-
url: string;
133-
html_url: string;
134-
followers_url: string;
135-
following_url: string;
136-
gists_url: string;
137-
starred_url: string;
138-
subscriptions_url: string;
139-
organizations_url: string;
140-
repos_url: string;
141-
events_url: string;
142-
received_events_url: string;
143-
type: string;
144-
site_admin: boolean;
145-
};
146-
base: { ref: string };
147-
};
148-
149-
const data = ref<PullRequest[] | undefined>(undefined);
122+
const pullRequests = ref<(PullRequest & ReviewStatus)[] | undefined>(undefined);
150123
const octokit = new Octokit();
151124
152125
function colorIsDarkSimple(bgColor: string): boolean {
@@ -188,24 +161,89 @@ function isMoreThanXdays(dateString: string, x: number): boolean {
188161
return days > x;
189162
}
190163
191-
async function getPrs() {
192-
octokit.rest.pulls
164+
async function getPrs(): Promise<void> {
165+
return octokit.rest.pulls
193166
.list({
194167
owner: 'LycheeOrg',
195168
repo: 'Lychee',
196169
})
197170
.then((response) => {
198-
data.value = response.data as unknown as PullRequest[];
199-
console.log('Pull requests fetched successfully:', data.value);
171+
pullRequests.value = response.data as unknown as PullRequest[];
200172
})
201173
.catch((error) => {
202174
console.error('Error fetching pull requests:', error);
203175
});
204-
// const response = await fetch('https://api.github.com/repos/LycheeOrg/Lychee/pulls');
205-
// data.value = await response.json();
206176
}
207177
208-
onMounted(() => {
209-
getPrs();
178+
async function getStatuses() {
179+
if (!pullRequests.value || pullRequests.value.length === 0) {
180+
console.warn('No pull requests available to fetch statuses for.');
181+
return;
182+
}
183+
184+
pullRequests.value.forEach(async (pr, idx) => {
185+
await octokit.rest.pulls
186+
.listReviews({
187+
owner: 'LycheeOrg',
188+
repo: 'Lychee',
189+
pull_number: pr.number, // Use the pull request number from the fetched PRs
190+
})
191+
.then((response) => {
192+
console.log(
193+
`Pull request reviews for PR #${pr.number} fetched successfully:`,
194+
response.data,
195+
);
196+
if (!pullRequests.value) {
197+
console.warn('pullRequests.value is undefined, cannot update review status.');
198+
return;
199+
}
200+
201+
pullRequests.value[idx].review = extractStatusForPr(
202+
response.data as PullRequestReview[],
203+
).review;
204+
})
205+
.catch((error) => {
206+
console.error(`Error fetching pull request reviews for PR #${pr.number}:`, error);
207+
});
208+
});
209+
}
210+
211+
function extractStatusForPr(reviews: PullRequestReview[]): ReviewStatus {
212+
// Loop through the reviews.
213+
// Ignore all the reviews that are not from the contributors.
214+
const statuses = reviews.reduce(
215+
(acc, review) => {
216+
if (
217+
(review.state === APPROVED || review.state === CHANGES_REQUESTED) &&
218+
review.author_association === CONTRIBUTOR
219+
) {
220+
acc[review.user.login] = { status: review.state, user: review.user };
221+
}
222+
return acc;
223+
},
224+
{} as Record<string, { status: string; user: User }>,
225+
);
226+
227+
const result: ReviewStatus = {
228+
review: {
229+
approved: false,
230+
changes_requested: false,
231+
by: [],
232+
},
233+
};
234+
Object.entries(statuses).forEach(([_no, review]) => {
235+
if (review.status === APPROVED) {
236+
result.review!.approved = true;
237+
} else if (status === CHANGES_REQUESTED) {
238+
result.review!.changes_requested = true;
239+
}
240+
result.review!.by.push(review.user);
241+
});
242+
return result;
243+
}
244+
245+
onMounted(async () => {
246+
await getPrs();
247+
getStatuses();
210248
});
211249
</script>

src/ResponsesTypes.ts

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
export type User = {
2+
html_url: string;
3+
avatar_url: string;
4+
login: string;
5+
};
6+
7+
export type PullRequest = {
8+
active_lock_reason: unknown;
9+
assignee: unknown;
10+
assignees: string[];
11+
author_association: string;
12+
auto_merge: null;
13+
body: string;
14+
closed_at: null;
15+
comments_url: string;
16+
commits_url: string;
17+
created_at: string;
18+
diff_url: string;
19+
draft: boolean;
20+
html_url: string;
21+
id: number;
22+
issue_url: string;
23+
labels: { color: string; id: number; description: string; name: string }[];
24+
locked: boolean;
25+
merge_commit_sha: string;
26+
merged_at: null;
27+
milestone: null;
28+
node_id: string;
29+
number: number;
30+
patch_url: string;
31+
requested_reviewers: [];
32+
review_comment_url: string;
33+
review_comments_url: string;
34+
state: 'open';
35+
statuses_url: string;
36+
title: string;
37+
updated_at: string;
38+
url: string;
39+
user: User;
40+
base: { ref: string };
41+
};
42+
43+
export const CONTRIBUTOR = 'CONTRIBUTOR';
44+
export const APPROVED = 'APPROVED';
45+
export const CHANGES_REQUESTED = 'CHANGES_REQUESTED';
46+
export type PullRequestReview = {
47+
author_association: string;
48+
body: string;
49+
commit_id: string;
50+
html_url: string;
51+
id: number;
52+
node_id: string;
53+
pull_request_url: string;
54+
state: string;
55+
submitted_at: string;
56+
user: User;
57+
};
58+
59+
export type ReviewStatus = {
60+
review?: {
61+
approved: boolean;
62+
changes_requested: boolean;
63+
by: User[];
64+
};
65+
};

src/components/UserTag.vue

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
<template>
2+
<a :href="props.user.html_url" class="text-muted-color-emphasis">
3+
<img :src="props.user.avatar_url" class="rounded-full h-4 inline" /> {{ props.user.login }}
4+
</a>
5+
</template>
6+
<script setup lang="ts">
7+
const props = defineProps<{
8+
user: {
9+
html_url: string;
10+
avatar_url: string;
11+
login: string;
12+
};
13+
}>();
14+
</script>

0 commit comments

Comments
 (0)