2024-02-24 02:41:24 +03:00
< script >
import { SvgIcon } from '../svg.js' ;
import {
Chart ,
Legend ,
LinearScale ,
TimeScale ,
PointElement ,
LineElement ,
Filler ,
} from 'chart.js' ;
import { GET } from '../modules/fetch.js' ;
import { Line as ChartLine } from 'vue-chartjs' ;
import {
startDaysBetween ,
firstStartDateAfterDate ,
fillEmptyStartDaysWithZeroes ,
} from '../utils/time.js' ;
import { chartJsColors } from '../utils/color.js' ;
import { sleep } from '../utils.js' ;
import 'chartjs-adapter-dayjs-4/dist/chartjs-adapter-dayjs-4.esm' ;
const { pageData } = window . config ;
Chart . defaults . color = chartJsColors . text ;
Chart . defaults . borderColor = chartJsColors . border ;
Chart . register (
TimeScale ,
LinearScale ,
Legend ,
PointElement ,
LineElement ,
Filler ,
) ;
export default {
components : { ChartLine , SvgIcon } ,
props : {
locale : {
type : Object ,
2024-03-22 15:06:53 +01:00
required : true ,
2024-02-24 02:41:24 +03:00
} ,
} ,
data : ( ) => ( {
isLoading : false ,
errorText : '' ,
repoLink : pageData . repoLink || [ ] ,
data : [ ] ,
} ) ,
mounted ( ) {
this . fetchGraphData ( ) ;
} ,
methods : {
async fetchGraphData ( ) {
this . isLoading = true ;
try {
let response ;
do {
response = await GET ( ` ${ this . repoLink } /activity/code-frequency/data ` ) ;
if ( response . status === 202 ) {
await sleep ( 1000 ) ; // wait for 1 second before retrying
}
} while ( response . status === 202 ) ;
if ( response . ok ) {
this . data = await response . json ( ) ;
const weekValues = Object . values ( this . data ) ;
const start = weekValues [ 0 ] . week ;
const end = firstStartDateAfterDate ( new Date ( ) ) ;
const startDays = startDaysBetween ( new Date ( start ) , new Date ( end ) ) ;
this . data = fillEmptyStartDaysWithZeroes ( startDays , this . data ) ;
this . errorText = '' ;
} else {
this . errorText = response . statusText ;
}
} catch ( err ) {
this . errorText = err . message ;
} finally {
this . isLoading = false ;
}
} ,
toGraphData ( data ) {
return {
datasets : [
{
data : data . map ( ( i ) => ( { x : i . week , y : i . additions } ) ) ,
pointRadius : 0 ,
pointHitRadius : 0 ,
fill : true ,
label : 'Additions' ,
backgroundColor : chartJsColors [ 'additions' ] ,
borderWidth : 0 ,
tension : 0.3 ,
} ,
{
data : data . map ( ( i ) => ( { x : i . week , y : - i . deletions } ) ) ,
pointRadius : 0 ,
pointHitRadius : 0 ,
fill : true ,
label : 'Deletions' ,
backgroundColor : chartJsColors [ 'deletions' ] ,
borderWidth : 0 ,
tension : 0.3 ,
} ,
] ,
} ;
} ,
getOptions ( ) {
return {
responsive : true ,
maintainAspectRatio : false ,
animation : true ,
plugins : {
legend : {
display : true ,
} ,
} ,
scales : {
x : {
type : 'time' ,
grid : {
display : false ,
} ,
time : {
minUnit : 'month' ,
} ,
ticks : {
maxRotation : 0 ,
2024-03-22 15:06:53 +01:00
maxTicksLimit : 12 ,
2024-02-24 02:41:24 +03:00
} ,
} ,
y : {
ticks : {
2024-03-22 15:06:53 +01:00
maxTicksLimit : 6 ,
2024-02-24 02:41:24 +03:00
} ,
} ,
} ,
} ;
} ,
} ,
} ;
< / script >
< 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 02:41:24 +03:00
{ { isLoading ? locale . loadingTitle : errorText ? locale . loadingTitleFailed : ` Code frequency over the history of ${ repoLink . slice ( 1 ) } ` } }
< / 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 02:41:24 +03:00
< div v-if = "isLoading" >
< SvgIcon name = "octicon-sync" class = "gt-mr-3 job-status-rotate" / >
{ { locale . loadingInfo } }
< / div >
< div v -else class = "text red" >
< SvgIcon name = "octicon-x-circle-fill" / >
{ { errorText } }
< / div >
< / div >
< ChartLine
v - memo = "data" v - if = "data.length !== 0"
: data = "toGraphData(data)" : options = "getOptions()"
/ >
< / div >
< / div >
< / template >
< style scoped >
. main - graph {
height : 440 px ;
}
< / style >