mirror of
				https://github.com/go-gitea/gitea.git
				synced 2025-11-03 20:36:07 +01:00 
			
		
		
		
	Implement recent commits graph (#29210)
This is the implementation of Recent Commits page. This feature was mentioned on #18262. It adds another tab to Activity page called Recent Commits. Recent Commits tab shows number of commits since last year for the repository.
This commit is contained in:
		
							
								
								
									
										149
									
								
								web_src/js/components/RepoRecentCommits.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										149
									
								
								web_src/js/components/RepoRecentCommits.vue
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,149 @@
 | 
			
		||||
<script>
 | 
			
		||||
import {SvgIcon} from '../svg.js';
 | 
			
		||||
import {
 | 
			
		||||
  Chart,
 | 
			
		||||
  Tooltip,
 | 
			
		||||
  BarElement,
 | 
			
		||||
  LinearScale,
 | 
			
		||||
  TimeScale,
 | 
			
		||||
} from 'chart.js';
 | 
			
		||||
import {GET} from '../modules/fetch.js';
 | 
			
		||||
import {Bar} 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,
 | 
			
		||||
  BarElement,
 | 
			
		||||
  Tooltip,
 | 
			
		||||
);
 | 
			
		||||
 | 
			
		||||
export default {
 | 
			
		||||
  components: {Bar, 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/recent-commits/data`);
 | 
			
		||||
          if (response.status === 202) {
 | 
			
		||||
            await sleep(1000); // wait for 1 second before retrying
 | 
			
		||||
          }
 | 
			
		||||
        } while (response.status === 202);
 | 
			
		||||
        if (response.ok) {
 | 
			
		||||
          const data = await response.json();
 | 
			
		||||
          const start = Object.values(data)[0].week;
 | 
			
		||||
          const end = firstStartDateAfterDate(new Date());
 | 
			
		||||
          const startDays = startDaysBetween(new Date(start), new Date(end));
 | 
			
		||||
          this.data = fillEmptyStartDaysWithZeroes(startDays, data).slice(-52);
 | 
			
		||||
          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.commits})),
 | 
			
		||||
            label: 'Commits',
 | 
			
		||||
            backgroundColor: chartJsColors['commits'],
 | 
			
		||||
            borderWidth: 0,
 | 
			
		||||
            tension: 0.3,
 | 
			
		||||
          },
 | 
			
		||||
        ],
 | 
			
		||||
      };
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    getOptions() {
 | 
			
		||||
      return {
 | 
			
		||||
        responsive: true,
 | 
			
		||||
        maintainAspectRatio: false,
 | 
			
		||||
        animation: true,
 | 
			
		||||
        scales: {
 | 
			
		||||
          x: {
 | 
			
		||||
            type: 'time',
 | 
			
		||||
            grid: {
 | 
			
		||||
              display: false,
 | 
			
		||||
            },
 | 
			
		||||
            time: {
 | 
			
		||||
              minUnit: 'week',
 | 
			
		||||
            },
 | 
			
		||||
            ticks: {
 | 
			
		||||
              maxRotation: 0,
 | 
			
		||||
              maxTicksLimit: 52
 | 
			
		||||
            },
 | 
			
		||||
          },
 | 
			
		||||
          y: {
 | 
			
		||||
            ticks: {
 | 
			
		||||
              maxTicksLimit: 6
 | 
			
		||||
            },
 | 
			
		||||
          },
 | 
			
		||||
        },
 | 
			
		||||
      };
 | 
			
		||||
    },
 | 
			
		||||
  },
 | 
			
		||||
};
 | 
			
		||||
</script>
 | 
			
		||||
<template>
 | 
			
		||||
  <div>
 | 
			
		||||
    <div class="ui header gt-df gt-ac gt-sb">
 | 
			
		||||
      {{ isLoading ? locale.loadingTitle : errorText ? locale.loadingTitleFailed: "Number of commits in the past year" }}
 | 
			
		||||
    </div>
 | 
			
		||||
    <div class="gt-df ui segment main-graph">
 | 
			
		||||
      <div v-if="isLoading || errorText !== ''" class="gt-tc gt-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>
 | 
			
		||||
      <Bar
 | 
			
		||||
        v-memo="data" v-if="data.length !== 0"
 | 
			
		||||
        :data="toGraphData(data)" :options="getOptions()"
 | 
			
		||||
      />
 | 
			
		||||
    </div>
 | 
			
		||||
  </div>
 | 
			
		||||
</template>
 | 
			
		||||
<style scoped>
 | 
			
		||||
.main-graph {
 | 
			
		||||
  height: 250px;
 | 
			
		||||
}
 | 
			
		||||
</style>
 | 
			
		||||
							
								
								
									
										21
									
								
								web_src/js/features/recent-commits.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										21
									
								
								web_src/js/features/recent-commits.js
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,21 @@
 | 
			
		||||
import {createApp} from 'vue';
 | 
			
		||||
 | 
			
		||||
export async function initRepoRecentCommits() {
 | 
			
		||||
  const el = document.getElementById('repo-recent-commits-chart');
 | 
			
		||||
  if (!el) return;
 | 
			
		||||
 | 
			
		||||
  const {default: RepoRecentCommits} = await import(/* webpackChunkName: "recent-commits-graph" */'../components/RepoRecentCommits.vue');
 | 
			
		||||
  try {
 | 
			
		||||
    const View = createApp(RepoRecentCommits, {
 | 
			
		||||
      locale: {
 | 
			
		||||
        loadingTitle: el.getAttribute('data-locale-loading-title'),
 | 
			
		||||
        loadingTitleFailed: el.getAttribute('data-locale-loading-title-failed'),
 | 
			
		||||
        loadingInfo: el.getAttribute('data-locale-loading-info'),
 | 
			
		||||
      }
 | 
			
		||||
    });
 | 
			
		||||
    View.mount(el);
 | 
			
		||||
  } catch (err) {
 | 
			
		||||
    console.error('RepoRecentCommits failed to load', err);
 | 
			
		||||
    el.textContent = el.getAttribute('data-locale-component-failed-to-load');
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
@@ -85,6 +85,7 @@ import {initRepoIssueList} from './features/repo-issue-list.js';
 | 
			
		||||
import {initCommonIssueListQuickGoto} from './features/common-issue-list.js';
 | 
			
		||||
import {initRepoContributors} from './features/contributors.js';
 | 
			
		||||
import {initRepoCodeFrequency} from './features/code-frequency.js';
 | 
			
		||||
import {initRepoRecentCommits} from './features/recent-commits.js';
 | 
			
		||||
import {initRepoDiffCommitBranchesAndTags} from './features/repo-diff-commit.js';
 | 
			
		||||
import {initDirAuto} from './modules/dirauto.js';
 | 
			
		||||
 | 
			
		||||
@@ -176,6 +177,7 @@ onDomReady(() => {
 | 
			
		||||
  initRepositoryActionView();
 | 
			
		||||
  initRepoContributors();
 | 
			
		||||
  initRepoCodeFrequency();
 | 
			
		||||
  initRepoRecentCommits();
 | 
			
		||||
 | 
			
		||||
  initCommitStatuses();
 | 
			
		||||
  initCaptcha();
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user