You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

RepoCodeFrequency.vue 4.2KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172
  1. <script>
  2. import {SvgIcon} from '../svg.js';
  3. import {
  4. Chart,
  5. Legend,
  6. LinearScale,
  7. TimeScale,
  8. PointElement,
  9. LineElement,
  10. Filler,
  11. } from 'chart.js';
  12. import {GET} from '../modules/fetch.js';
  13. import {Line as ChartLine} from 'vue-chartjs';
  14. import {
  15. startDaysBetween,
  16. firstStartDateAfterDate,
  17. fillEmptyStartDaysWithZeroes,
  18. } from '../utils/time.js';
  19. import {chartJsColors} from '../utils/color.js';
  20. import {sleep} from '../utils.js';
  21. import 'chartjs-adapter-dayjs-4/dist/chartjs-adapter-dayjs-4.esm';
  22. const {pageData} = window.config;
  23. Chart.defaults.color = chartJsColors.text;
  24. Chart.defaults.borderColor = chartJsColors.border;
  25. Chart.register(
  26. TimeScale,
  27. LinearScale,
  28. Legend,
  29. PointElement,
  30. LineElement,
  31. Filler,
  32. );
  33. export default {
  34. components: {ChartLine, 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/code-frequency/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. this.data = await response.json();
  63. const weekValues = Object.values(this.data);
  64. const start = weekValues[0].week;
  65. const end = firstStartDateAfterDate(new Date());
  66. const startDays = startDaysBetween(new Date(start), new Date(end));
  67. this.data = fillEmptyStartDaysWithZeroes(startDays, this.data);
  68. this.errorText = '';
  69. } else {
  70. this.errorText = response.statusText;
  71. }
  72. } catch (err) {
  73. this.errorText = err.message;
  74. } finally {
  75. this.isLoading = false;
  76. }
  77. },
  78. toGraphData(data) {
  79. return {
  80. datasets: [
  81. {
  82. data: data.map((i) => ({x: i.week, y: i.additions})),
  83. pointRadius: 0,
  84. pointHitRadius: 0,
  85. fill: true,
  86. label: 'Additions',
  87. backgroundColor: chartJsColors['additions'],
  88. borderWidth: 0,
  89. tension: 0.3,
  90. },
  91. {
  92. data: data.map((i) => ({x: i.week, y: -i.deletions})),
  93. pointRadius: 0,
  94. pointHitRadius: 0,
  95. fill: true,
  96. label: 'Deletions',
  97. backgroundColor: chartJsColors['deletions'],
  98. borderWidth: 0,
  99. tension: 0.3,
  100. },
  101. ],
  102. };
  103. },
  104. getOptions() {
  105. return {
  106. responsive: true,
  107. maintainAspectRatio: false,
  108. animation: true,
  109. plugins: {
  110. legend: {
  111. display: true,
  112. },
  113. },
  114. scales: {
  115. x: {
  116. type: 'time',
  117. grid: {
  118. display: false,
  119. },
  120. time: {
  121. minUnit: 'month',
  122. },
  123. ticks: {
  124. maxRotation: 0,
  125. maxTicksLimit: 12,
  126. },
  127. },
  128. y: {
  129. ticks: {
  130. maxTicksLimit: 6,
  131. },
  132. },
  133. },
  134. };
  135. },
  136. },
  137. };
  138. </script>
  139. <template>
  140. <div>
  141. <div class="ui header tw-flex tw-content-center tw-justify-between">
  142. {{ isLoading ? locale.loadingTitle : errorText ? locale.loadingTitleFailed: `Code frequency over the history of ${repoLink.slice(1)}` }}
  143. </div>
  144. <div class="tw-flex ui segment main-graph">
  145. <div v-if="isLoading || errorText !== ''" class="gt-tc tw-m-auto">
  146. <div v-if="isLoading">
  147. <SvgIcon name="octicon-sync" class="gt-mr-3 job-status-rotate"/>
  148. {{ locale.loadingInfo }}
  149. </div>
  150. <div v-else class="text red">
  151. <SvgIcon name="octicon-x-circle-fill"/>
  152. {{ errorText }}
  153. </div>
  154. </div>
  155. <ChartLine
  156. v-memo="data" v-if="data.length !== 0"
  157. :data="toGraphData(data)" :options="getOptions()"
  158. />
  159. </div>
  160. </div>
  161. </template>
  162. <style scoped>
  163. .main-graph {
  164. height: 440px;
  165. }
  166. </style>