mirror of https://github.com/go-gitea/gitea.git
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
150 lines
3.5 KiB
150 lines
3.5 KiB
<script lang="ts" setup> |
|
import {SvgIcon} from '../svg.ts'; |
|
import { |
|
Chart, |
|
Tooltip, |
|
BarElement, |
|
LinearScale, |
|
TimeScale, |
|
type ChartOptions, |
|
type ChartData, |
|
} from 'chart.js'; |
|
import {GET} from '../modules/fetch.ts'; |
|
import {Bar} from 'vue-chartjs'; |
|
import { |
|
startDaysBetween, |
|
firstStartDateAfterDate, |
|
fillEmptyStartDaysWithZeroes, |
|
type DayData, |
|
type DayDataObject, |
|
} from '../utils/time.ts'; |
|
import {chartJsColors} from '../utils/color.ts'; |
|
import {sleep} from '../utils.ts'; |
|
import 'chartjs-adapter-dayjs-4/dist/chartjs-adapter-dayjs-4.esm'; |
|
import {onMounted, ref} from 'vue'; |
|
|
|
const {pageData} = window.config; |
|
|
|
Chart.defaults.color = chartJsColors.text; |
|
Chart.defaults.borderColor = chartJsColors.border; |
|
|
|
Chart.register( |
|
TimeScale, |
|
LinearScale, |
|
BarElement, |
|
Tooltip, |
|
); |
|
|
|
defineProps<{ |
|
locale: { |
|
loadingTitle: string; |
|
loadingTitleFailed: string; |
|
loadingInfo: string; |
|
}; |
|
}>(); |
|
|
|
const isLoading = ref(false); |
|
const errorText = ref(''); |
|
const repoLink = ref(pageData.repoLink || []); |
|
const data = ref<DayData[]>([]); |
|
|
|
onMounted(() => { |
|
fetchGraphData(); |
|
}); |
|
|
|
async function fetchGraphData() { |
|
isLoading.value = true; |
|
try { |
|
let response: Response; |
|
do { |
|
response = await GET(`${repoLink.value}/activity/recent-commits/data`); |
|
if (response.status === 202) { |
|
await sleep(1000); // wait for 1 second before retrying |
|
} |
|
} while (response.status === 202); |
|
if (response.ok) { |
|
const dayDataObj: DayDataObject = await response.json(); |
|
const start = Object.values(dayDataObj)[0].week; |
|
const end = firstStartDateAfterDate(new Date()); |
|
const startDays = startDaysBetween(start, end); |
|
data.value = fillEmptyStartDaysWithZeroes(startDays, dayDataObj).slice(-52); |
|
errorText.value = ''; |
|
} else { |
|
errorText.value = response.statusText; |
|
} |
|
} catch (err) { |
|
errorText.value = err.message; |
|
} finally { |
|
isLoading.value = false; |
|
} |
|
} |
|
|
|
function toGraphData(data: DayData[]): ChartData<'bar'> { |
|
return { |
|
datasets: [ |
|
{ |
|
// @ts-expect-error -- bar chart expects one-dimensional data, but apparently x/y still works |
|
data: data.map((i) => ({x: i.week, y: i.commits})), |
|
label: 'Commits', |
|
backgroundColor: chartJsColors['commits'], |
|
borderWidth: 0, |
|
tension: 0.3, |
|
}, |
|
], |
|
}; |
|
} |
|
|
|
const options: ChartOptions<'bar'> = { |
|
responsive: true, |
|
maintainAspectRatio: false, |
|
scales: { |
|
x: { |
|
type: 'time', |
|
grid: { |
|
display: false, |
|
}, |
|
time: { |
|
minUnit: 'week', |
|
}, |
|
ticks: { |
|
maxRotation: 0, |
|
maxTicksLimit: 52, |
|
}, |
|
}, |
|
y: { |
|
ticks: { |
|
maxTicksLimit: 6, |
|
}, |
|
}, |
|
}, |
|
} satisfies ChartOptions; |
|
</script> |
|
|
|
<template> |
|
<div> |
|
<div class="ui header tw-flex tw-items-center tw-justify-between"> |
|
{{ isLoading ? locale.loadingTitle : errorText ? locale.loadingTitleFailed: "Number of commits in the past year" }} |
|
</div> |
|
<div class="tw-flex ui segment main-graph"> |
|
<div v-if="isLoading || errorText !== ''" class="gt-tc tw-m-auto"> |
|
<div v-if="isLoading"> |
|
<SvgIcon name="octicon-sync" class="tw-mr-2 circular-spin"/> |
|
{{ locale.loadingInfo }} |
|
</div> |
|
<div v-else class="text red"> |
|
<SvgIcon name="octicon-x-circle-fill"/> |
|
{{ errorText }} |
|
</div> |
|
</div> |
|
<Bar |
|
v-memo="data" v-if="data.length !== 0" |
|
:data="toGraphData(data)" :options="options" |
|
/> |
|
</div> |
|
</div> |
|
</template> |
|
<style scoped> |
|
.main-graph { |
|
height: 250px; |
|
} |
|
</style>
|
|
|