2024-10-28 21:15:05 +01:00
< script lang = "ts" setup >
2024-07-07 17:32:30 +02:00
import { SvgIcon } from '../svg.ts' ;
2024-02-24 13:22:51 +03:00
import {
Chart ,
Tooltip ,
BarElement ,
LinearScale ,
TimeScale ,
2024-10-28 21:15:05 +01:00
type ChartOptions ,
2024-12-08 03:58:18 +01:00
type ChartData ,
2024-02-24 13:22:51 +03:00
} from 'chart.js' ;
2024-07-07 17:32:30 +02:00
import { GET } from '../modules/fetch.ts' ;
2024-02-24 13:22:51 +03:00
import { Bar } from 'vue-chartjs' ;
import {
startDaysBetween ,
firstStartDateAfterDate ,
fillEmptyStartDaysWithZeroes ,
2024-10-28 21:15:05 +01:00
type DayData ,
2024-12-08 03:58:18 +01:00
type DayDataObject ,
2024-07-07 17:32:30 +02:00
} from '../utils/time.ts' ;
import { chartJsColors } from '../utils/color.ts' ;
import { sleep } from '../utils.ts' ;
2024-02-24 13:22:51 +03:00
import 'chartjs-adapter-dayjs-4/dist/chartjs-adapter-dayjs-4.esm' ;
2024-10-28 21:15:05 +01:00
import { onMounted , ref } from 'vue' ;
2024-02-24 13:22:51 +03:00
const { pageData } = window . config ;
Chart . defaults . color = chartJsColors . text ;
Chart . defaults . borderColor = chartJsColors . border ;
Chart . register (
TimeScale ,
LinearScale ,
BarElement ,
Tooltip ,
) ;
2024-10-28 21:15:05 +01:00
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
2024-02-24 13:22:51 +03:00
}
2024-10-28 21:15:05 +01:00
} while ( response . status === 202 ) ;
if ( response . ok ) {
2024-12-08 03:58:18 +01:00
const dayDataObj : DayDataObject = await response . json ( ) ;
const start = Object . values ( dayDataObj ) [ 0 ] . week ;
2024-10-28 21:15:05 +01:00
const end = firstStartDateAfterDate ( new Date ( ) ) ;
const startDays = startDaysBetween ( start , end ) ;
2024-12-08 03:58:18 +01:00
data . value = fillEmptyStartDaysWithZeroes ( startDays , dayDataObj ) . slice ( - 52 ) ;
2024-10-28 21:15:05 +01:00
errorText . value = '' ;
} else {
errorText . value = response . statusText ;
}
} catch ( err ) {
errorText . value = err . message ;
} finally {
isLoading . value = false ;
}
}
2024-02-24 13:22:51 +03:00
2024-12-08 03:58:18 +01:00
function toGraphData ( data : DayData [ ] ) : ChartData < 'bar' > {
2024-10-28 21:15:05 +01:00
return {
datasets : [
{
2024-12-08 03:58:18 +01:00
// @ts-expect-error -- bar chart expects one-dimensional data, but apparently x/y still works
2024-10-28 21:15:05 +01:00
data : data . map ( ( i ) => ( { x : i . week , y : i . commits } ) ) ,
label : 'Commits' ,
backgroundColor : chartJsColors [ 'commits' ] ,
borderWidth : 0 ,
tension : 0.3 ,
} ,
] ,
} ;
}
2024-02-24 13:22:51 +03:00
2024-12-08 03:58:18 +01:00
const options : ChartOptions < 'bar' > = {
2024-10-28 21:15:05 +01:00
responsive : true ,
maintainAspectRatio : false ,
scales : {
x : {
type : 'time' ,
grid : {
display : false ,
} ,
time : {
minUnit : 'week' ,
} ,
ticks : {
maxRotation : 0 ,
maxTicksLimit : 52 ,
} ,
} ,
y : {
ticks : {
maxTicksLimit : 6 ,
} ,
2024-02-24 13:22:51 +03:00
} ,
} ,
2024-10-28 21:15:05 +01:00
} satisfies ChartOptions ;
2024-02-24 13:22:51 +03:00
< / script >
2024-10-28 21:15:05 +01:00
2024-02-24 13:22:51 +03:00
< template >
< div >
2024-03-22 20:51:29 +01:00
< div class = "ui header tw-flex tw-items-center tw-justify-between" >
2024-02-24 13:22:51 +03:00
{ { isLoading ? locale . loadingTitle : errorText ? locale . loadingTitleFailed : "Number of commits in the past year" } }
< / div >
2024-03-22 14:45:10 +01:00
< div class = "tw-flex ui segment main-graph" >
2024-03-18 15:47:05 +01:00
< div v-if = "isLoading || errorText !== ''" class="gt-tc tw-m-auto" >
2024-02-24 13:22:51 +03:00
< div v-if = "isLoading" >
Migrate margin and padding helpers to tailwind (#30043)
This will conclude the refactor of 1:1 class replacements to tailwind,
except `gt-hidden`. Commands ran:
```bash
perl -p -i -e 's#gt-(p|m)([lrtbxy])?-0#tw-$1$2-0#g' {web_src/js,templates,routers,services}/**/*
perl -p -i -e 's#gt-(p|m)([lrtbxy])?-1#tw-$1$2-0.5#g' {web_src/js,templates,routers,services}/**/*
perl -p -i -e 's#gt-(p|m)([lrtbxy])?-2#tw-$1$2-1#g' {web_src/js,templates,routers,services}/**/*
perl -p -i -e 's#gt-(p|m)([lrtbxy])?-3#tw-$1$2-2#g' {web_src/js,templates,routers,services}/**/*
perl -p -i -e 's#gt-(p|m)([lrtbxy])?-4#tw-$1$2-4#g' {web_src/js,templates,routers,services}/**/*
perl -p -i -e 's#gt-(p|m)([lrtbxy])?-5#tw-$1$2-8#g' {web_src/js,templates,routers,services}/**/*
```
2024-03-24 17:42:49 +01:00
< SvgIcon name = "octicon-sync" class = "tw-mr-2 job-status-rotate" / >
2024-02-24 13:22:51 +03:00
{ { 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"
2024-10-28 21:15:05 +01:00
: data = "toGraphData(data)" : options = "options"
2024-02-24 13:22:51 +03:00
/ >
< / div >
< / div >
< / template >
< style scoped >
. main - graph {
height : 250 px ;
}
< / style >