Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,88 @@
<h3 class="text-heading-3 font-semibold font-secondary pb-3">
Review time by pull request size
</h3>
<p class="text-body-2 text-neutral-500">
<p class="text-body-2 text-neutral-500 mb-6">
Active contributor is an individual performing tasks such as commits,
issues, or pull requests during the selected time period.
</p>
Comment on lines +6 to 9
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Update description to match component purpose.

The current description discusses "active contributors" which doesn't align with the component's purpose of displaying review time by pull request size. Consider updating the text to describe what this widget actually shows.

-    <p class="text-body-2 text-neutral-500 mb-6">
-      Active contributor is an individual performing tasks such as commits,
-      issues, or pull requests during the selected time period.
-    </p>
+    <p class="text-body-2 text-neutral-500 mb-6">
+      This chart shows the average review time for pull requests grouped by size (number of lines).
+      Longer bars indicate longer review times for that size category.
+    </p>
📝 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
<p class="text-body-2 text-neutral-500 mb-6">
Active contributor is an individual performing tasks such as commits,
issues, or pull requests during the selected time period.
</p>
<p class="text-body-2 text-neutral-500 mb-6">
This chart shows the average review time for pull requests grouped by size (number of lines).
Longer bars indicate longer review times for that size category.
</p>

<hr>
<section class="mt-5">
<div class="w-full min-h-[380px] my-5">
<div
v-if="status === 'success'"
class="flex flex-col gap-8 text-neutral-900 text-sm"
>
<div
v-for="item in reviewTimeByPr"
:key="item.sortId"
class="flex flex-col gap-2"
>
<div class="flex flex-row gap-2">
<span>{{ item.lines }} lines</span>
<span class="text-neutral-400">{{ item.prCount }} pull requests</span>
</div>
<div class="pr-4">
<lfx-progress-bar
:values="[item.averageReviewTime / maxValue * 100]"
:label="`${item.averageReviewTime} ${item.averageReviewTimeUnit}`"
hide-empty
/>
</div>
</div>
</div>
<lfx-spinner v-else />
</div>
</section>
</lfx-card>
</template>

<script setup lang="ts">
import LfxCard from "~/components/uikit/card/card.vue";
import { useFetch, useRoute } from 'nuxt/app';
import { computed, watch } from 'vue';
import type { ReviewTimeByPrItem } from './types/review-time-by-pr.types';
import LfxCard from '~/components/uikit/card/card.vue';
import useToastService from '~/components/uikit/toast/toast.service';
import { ToastTypesEnum } from '~/components/uikit/toast/types/toast.types';
import LfxSpinner from '~/components/uikit/spinner/spinner.vue';
import LfxProgressBar from '~/components/uikit/progress-bar/progress-bar.vue';

const props = withDefaults(
defineProps<{
timePeriod?: string;
}>(),
{
timePeriod: '90d'
}
);

const { showToast } = useToastService();

const route = useRoute();

const { data, status, error } = useFetch(
`/api/project/${route.params.slug}/development/review-time-by-pr-size`,
{
params: {
project: route.params.slug,
repository: route.params.name || '',
'time-period': props.timePeriod
}
}
);
const reviewTimeByPr = computed<ReviewTimeByPrItem[]>(() => data.value as ReviewTimeByPrItem[]);
const maxValue = computed(() => Math.max(...reviewTimeByPr.value.map((item) => item.averageReviewTime)));

watch(error, (err) => {
if (err) {
showToast(
`Error fetching social mentions: ${error.value?.statusMessage}`,
ToastTypesEnum.negative,
Comment on lines +79 to +81
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue

Fix incorrect error message text.

The error message mentions "social mentions" which doesn't match this component's purpose of displaying review times by pull request size. Update the message to be contextually correct.

    showToast(
-      `Error fetching social mentions: ${error.value?.statusMessage}`,
+      `Error fetching review time data: ${error.value?.statusMessage}`,
      ToastTypesEnum.negative,
📝 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
showToast(
`Error fetching social mentions: ${error.value?.statusMessage}`,
ToastTypesEnum.negative,
showToast(
`Error fetching review time data: ${error.value?.statusMessage}`,
ToastTypesEnum.negative,

undefined,
10000
);
}
});

</script>

<script lang="ts">
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
export interface ReviewTimeByPrItem {
sortId: number;
lines: string;
prCount: number;
averageReviewTime: number;
averageReviewTimeUnit: string;
}
3 changes: 3 additions & 0 deletions frontend/app/components/uikit/chart/helpers/chart-helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,9 @@ export const convertToChartData = (
} as ChartData)
) ?? [];

export const getMaxValue = (data: ChartData[]): number => data //
.reduce((max, item) => Math.max(max, item.values[0] ?? 0), 0);

export const convertToCategoryData = (
xData: ChartData[],
yData: ChartData[]
Expand Down
8 changes: 6 additions & 2 deletions frontend/app/components/uikit/progress-bar/progress-bar.scss
Original file line number Diff line number Diff line change
@@ -1,8 +1,12 @@
.c-progress-bar {
@apply flex flex-row items-stretch justify-between gap-0.5 h-2 w-full;
@apply flex flex-row items-center gap-0.5 w-full;

.c-progress-bar__value, .c-progress-bar__empty {
@apply rounded-xs transition-all;
@apply rounded-xs transition-all h-2;
}

.c-progress-bar__label {
@apply text-sm text-neutral-900 ml-2 whitespace-nowrap;
}

&--normal {
Expand Down
15 changes: 13 additions & 2 deletions frontend/app/components/uikit/progress-bar/progress-bar.vue
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,14 @@
class="c-progress-bar__value"
:style="{ width: `${value}%` }"
/>
<div class="c-progress-bar__empty" />
<div
v-if="props.label"
class="c-progress-bar__label"
>{{ props.label }}</div>
<div
v-if="!props.hideEmpty"
class="c-progress-bar__empty"
/>
</div>
</template>

Expand All @@ -19,9 +26,13 @@ const props = withDefaults(
values: number[];
// TODO: change this once we have the correct types
color?: ProgressBarType;
label?: string;
hideEmpty?: boolean;
}>(),
{
color: 'normal'
color: 'normal',
hideEmpty: false,
label: undefined
}
);
</script>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { reviewTimeByPr } from '~~/server/mocks/review-time-by-pr.mock';

/**
* Frontend expects the data to be in the following format:
* [
* {
* sortId: number;
* lines: string;
* prCount: number;
* averageReviewTime: number;
* averageReviewTimeUnit: string;
* }
* ]
*/
/**
* Query params:
* - project: string
* - repository: string
* - time-period: string // This is isn't defined yet, but we'll add '90d', '1y', '5y' for now
*/
export default defineEventHandler(async () => reviewTimeByPr);
37 changes: 37 additions & 0 deletions frontend/server/mocks/review-time-by-pr.mock.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
export const reviewTimeByPr = [
{
sortId: 1,
lines: '1-9',
prCount: 1000,
averageReviewTime: 9.86,
averageReviewTimeUnit: 'days'
},
{
sortId: 2,
lines: '10-59',
prCount: 1200,
averageReviewTime: 20.96,
averageReviewTimeUnit: 'days'
},
{
sortId: 3,
lines: '60-99',
prCount: 1100,
averageReviewTime: 31.32,
averageReviewTimeUnit: 'days'
},
{
sortId: 4,
lines: '100-499',
prCount: 1300,
averageReviewTime: 42.07,
averageReviewTimeUnit: 'days'
},
{
sortId: 5,
lines: '500+',
prCount: 900,
averageReviewTime: 55.9,
averageReviewTimeUnit: 'days'
}
];