code.gitea.io/gitea@v1.22.3/web_src/js/components/RepoRecentCommits.vue (about)

     1  <script>
     2  import {SvgIcon} from '../svg.js';
     3  import {
     4    Chart,
     5    Tooltip,
     6    BarElement,
     7    LinearScale,
     8    TimeScale,
     9  } from 'chart.js';
    10  import {GET} from '../modules/fetch.js';
    11  import {Bar} from 'vue-chartjs';
    12  import {
    13    startDaysBetween,
    14    firstStartDateAfterDate,
    15    fillEmptyStartDaysWithZeroes,
    16  } from '../utils/time.js';
    17  import {chartJsColors} from '../utils/color.js';
    18  import {sleep} from '../utils.js';
    19  import 'chartjs-adapter-dayjs-4/dist/chartjs-adapter-dayjs-4.esm';
    20  
    21  const {pageData} = window.config;
    22  
    23  Chart.defaults.color = chartJsColors.text;
    24  Chart.defaults.borderColor = chartJsColors.border;
    25  
    26  Chart.register(
    27    TimeScale,
    28    LinearScale,
    29    BarElement,
    30    Tooltip,
    31  );
    32  
    33  export default {
    34    components: {Bar, SvgIcon},
    35    props: {
    36      locale: {
    37        type: Object,
    38        required: true,
    39      },
    40    },
    41    data: () => ({
    42      isLoading: false,
    43      errorText: '',
    44      repoLink: pageData.repoLink || [],
    45      data: [],
    46    }),
    47    mounted() {
    48      this.fetchGraphData();
    49    },
    50    methods: {
    51      async fetchGraphData() {
    52        this.isLoading = true;
    53        try {
    54          let response;
    55          do {
    56            response = await GET(`${this.repoLink}/activity/recent-commits/data`);
    57            if (response.status === 202) {
    58              await sleep(1000); // wait for 1 second before retrying
    59            }
    60          } while (response.status === 202);
    61          if (response.ok) {
    62            const data = await response.json();
    63            const start = Object.values(data)[0].week;
    64            const end = firstStartDateAfterDate(new Date());
    65            const startDays = startDaysBetween(start, end);
    66            this.data = fillEmptyStartDaysWithZeroes(startDays, data).slice(-52);
    67            this.errorText = '';
    68          } else {
    69            this.errorText = response.statusText;
    70          }
    71        } catch (err) {
    72          this.errorText = err.message;
    73        } finally {
    74          this.isLoading = false;
    75        }
    76      },
    77  
    78      toGraphData(data) {
    79        return {
    80          datasets: [
    81            {
    82              data: data.map((i) => ({x: i.week, y: i.commits})),
    83              label: 'Commits',
    84              backgroundColor: chartJsColors['commits'],
    85              borderWidth: 0,
    86              tension: 0.3,
    87            },
    88          ],
    89        };
    90      },
    91  
    92      getOptions() {
    93        return {
    94          responsive: true,
    95          maintainAspectRatio: false,
    96          animation: true,
    97          scales: {
    98            x: {
    99              type: 'time',
   100              grid: {
   101                display: false,
   102              },
   103              time: {
   104                minUnit: 'week',
   105              },
   106              ticks: {
   107                maxRotation: 0,
   108                maxTicksLimit: 52,
   109              },
   110            },
   111            y: {
   112              ticks: {
   113                maxTicksLimit: 6,
   114              },
   115            },
   116          },
   117        };
   118      },
   119    },
   120  };
   121  </script>
   122  <template>
   123    <div>
   124      <div class="ui header tw-flex tw-items-center tw-justify-between">
   125        {{ isLoading ? locale.loadingTitle : errorText ? locale.loadingTitleFailed: "Number of commits in the past year" }}
   126      </div>
   127      <div class="tw-flex ui segment main-graph">
   128        <div v-if="isLoading || errorText !== ''" class="gt-tc tw-m-auto">
   129          <div v-if="isLoading">
   130            <SvgIcon name="octicon-sync" class="tw-mr-2 job-status-rotate"/>
   131            {{ locale.loadingInfo }}
   132          </div>
   133          <div v-else class="text red">
   134            <SvgIcon name="octicon-x-circle-fill"/>
   135            {{ errorText }}
   136          </div>
   137        </div>
   138        <Bar
   139          v-memo="data" v-if="data.length !== 0"
   140          :data="toGraphData(data)" :options="getOptions()"
   141        />
   142      </div>
   143    </div>
   144  </template>
   145  <style scoped>
   146  .main-graph {
   147    height: 250px;
   148  }
   149  </style>