3ea7437fa6
Fixes https://github.com/go-gitea/gitea/issues/30005. Regression from https://github.com/go-gitea/gitea/pull/29945. There was only once instance of `tw-content-center` before that PR, so I just ran below command and reverted that one instance. ```sh perl -p -i -e 's#tw-content-center#tw-items-center#g' web_src/js/**/* templates/**/* models/**/* tests/**/* ``` (cherry picked from commit 04f9ad056882fc3f21b247b16f84437adf0f36d8) Conflicts: templates/repo/diff/conversation.tmpl templates/repo/header.tmpl templates/repo/issue/filter_list.tmpl templates/repo/issue/view_content/conversation.tmpl templates/repo/wiki/view.tmpl web_src/js/components/DashboardRepoList.vue re-ran the command after discarding the Gitea changes to ensure all Forgejo files are also covered
173 lines
4.2 KiB
Vue
173 lines
4.2 KiB
Vue
<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,
|
|
required: true,
|
|
},
|
|
},
|
|
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,
|
|
maxTicksLimit: 12,
|
|
},
|
|
},
|
|
y: {
|
|
ticks: {
|
|
maxTicksLimit: 6,
|
|
},
|
|
},
|
|
},
|
|
};
|
|
},
|
|
},
|
|
};
|
|
</script>
|
|
<template>
|
|
<div>
|
|
<div class="ui header tw-flex tw-items-center tw-justify-between">
|
|
{{ isLoading ? locale.loadingTitle : errorText ? locale.loadingTitleFailed: `Code frequency over the history of ${repoLink.slice(1)}` }}
|
|
</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="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: 440px;
|
|
}
|
|
</style>
|