diff options
Diffstat (limited to 'app/assets/javascripts')
22 files changed, 247 insertions, 18 deletions
diff --git a/app/assets/javascripts/admin/statistics_panel/components/app.vue b/app/assets/javascripts/admin/statistics_panel/components/app.vue new file mode 100644 index 00000000000..29077d926cf --- /dev/null +++ b/app/assets/javascripts/admin/statistics_panel/components/app.vue @@ -0,0 +1,45 @@ +<script> +import { mapState, mapGetters, mapActions } from 'vuex'; +import { GlLoadingIcon } from '@gitlab/ui'; +import statisticsLabels from '../constants'; + +export default { + components: { + GlLoadingIcon, + }, + data() { + return { + statisticsLabels, + }; + }, + computed: { + ...mapState(['isLoading', 'statistics']), + ...mapGetters(['getStatistics']), + }, + mounted() { + this.fetchStatistics(); + }, + methods: { + ...mapActions(['fetchStatistics']), + }, +}; +</script> + +<template> + <div class="info-well"> + <div class="well-segment admin-well admin-well-statistics"> + <h4>{{ __('Statistics') }}</h4> + <gl-loading-icon v-if="isLoading" size="md" class="my-3" /> + <template v-else> + <p + v-for="statistic in getStatistics(statisticsLabels)" + :key="statistic.key" + class="js-stats" + > + {{ statistic.label }} + <span class="light float-right">{{ statistic.value }}</span> + </p> + </template> + </div> + </div> +</template> diff --git a/app/assets/javascripts/admin/statistics_panel/constants.js b/app/assets/javascripts/admin/statistics_panel/constants.js new file mode 100644 index 00000000000..2dce19a3894 --- /dev/null +++ b/app/assets/javascripts/admin/statistics_panel/constants.js @@ -0,0 +1,14 @@ +import { s__ } from '~/locale'; + +const statisticsLabels = { + forks: s__('AdminStatistics|Forks'), + issues: s__('AdminStatistics|Issues'), + mergeRequests: s__('AdminStatistics|Merge Requests'), + notes: s__('AdminStatistics|Notes'), + snippets: s__('AdminStatistics|Snippets'), + sshKeys: s__('AdminStatistics|SSH Keys'), + milestones: s__('AdminStatistics|Milestones'), + activeUsers: s__('AdminStatistics|Active Users'), +}; + +export default statisticsLabels; diff --git a/app/assets/javascripts/admin/statistics_panel/index.js b/app/assets/javascripts/admin/statistics_panel/index.js new file mode 100644 index 00000000000..39112e3ddc0 --- /dev/null +++ b/app/assets/javascripts/admin/statistics_panel/index.js @@ -0,0 +1,22 @@ +import Vue from 'vue'; +import StatisticsPanelApp from './components/app.vue'; +import createStore from './store'; + +export default function(el) { + if (!el) { + return false; + } + + const store = createStore(); + + return new Vue({ + el, + store, + components: { + StatisticsPanelApp, + }, + render(h) { + return h(StatisticsPanelApp); + }, + }); +} diff --git a/app/assets/javascripts/admin/statistics_panel/store/actions.js b/app/assets/javascripts/admin/statistics_panel/store/actions.js new file mode 100644 index 00000000000..537025f524c --- /dev/null +++ b/app/assets/javascripts/admin/statistics_panel/store/actions.js @@ -0,0 +1,28 @@ +import Api from '~/api'; +import { s__ } from '~/locale'; +import createFlash from '~/flash'; +import { convertObjectPropsToCamelCase } from '~/lib/utils/common_utils'; +import * as types from './mutation_types'; + +export const requestStatistics = ({ commit }) => commit(types.REQUEST_STATISTICS); + +export const fetchStatistics = ({ dispatch }) => { + dispatch('requestStatistics'); + + Api.adminStatistics() + .then(({ data }) => { + dispatch('receiveStatisticsSuccess', convertObjectPropsToCamelCase(data, { deep: true })); + }) + .catch(error => dispatch('receiveStatisticsError', error)); +}; + +export const receiveStatisticsSuccess = ({ commit }, statistics) => + commit(types.RECEIVE_STATISTICS_SUCCESS, statistics); + +export const receiveStatisticsError = ({ commit }, error) => { + commit(types.RECEIVE_STATISTICS_ERROR, error); + createFlash(s__('AdminDashboard|Error loading the statistics. Please try again')); +}; + +// prevent babel-plugin-rewire from generating an invalid default during karma tests +export default () => {}; diff --git a/app/assets/javascripts/admin/statistics_panel/store/getters.js b/app/assets/javascripts/admin/statistics_panel/store/getters.js new file mode 100644 index 00000000000..24437bc76bf --- /dev/null +++ b/app/assets/javascripts/admin/statistics_panel/store/getters.js @@ -0,0 +1,17 @@ +/** + * Merges the statisticsLabels with the state's data + * and returns an array of the following form: + * [{ key: "forks", label: "Forks", value: 50 }] + */ +export const getStatistics = state => labels => + Object.keys(labels).map(key => { + const result = { + key, + label: labels[key], + value: state.statistics && state.statistics[key] ? state.statistics[key] : null, + }; + return result; + }); + +// prevent babel-plugin-rewire from generating an invalid default during karma tests +export default () => {}; diff --git a/app/assets/javascripts/admin/statistics_panel/store/index.js b/app/assets/javascripts/admin/statistics_panel/store/index.js new file mode 100644 index 00000000000..ece9e6419dd --- /dev/null +++ b/app/assets/javascripts/admin/statistics_panel/store/index.js @@ -0,0 +1,16 @@ +import Vue from 'vue'; +import Vuex from 'vuex'; +import * as actions from './actions'; +import * as getters from './getters'; +import mutations from './mutations'; +import state from './state'; + +Vue.use(Vuex); + +export default () => + new Vuex.Store({ + actions, + getters, + mutations, + state: state(), + }); diff --git a/app/assets/javascripts/admin/statistics_panel/store/mutation_types.js b/app/assets/javascripts/admin/statistics_panel/store/mutation_types.js new file mode 100644 index 00000000000..4e0ca4ed3cd --- /dev/null +++ b/app/assets/javascripts/admin/statistics_panel/store/mutation_types.js @@ -0,0 +1,3 @@ +export const REQUEST_STATISTICS = 'REQUEST_STATISTICS'; +export const RECEIVE_STATISTICS_SUCCESS = 'RECEIVE_STATISTICS_SUCCESS'; +export const RECEIVE_STATISTICS_ERROR = 'RECEIVE_STATISTICS_ERROR'; diff --git a/app/assets/javascripts/admin/statistics_panel/store/mutations.js b/app/assets/javascripts/admin/statistics_panel/store/mutations.js new file mode 100644 index 00000000000..d0fac5cfbab --- /dev/null +++ b/app/assets/javascripts/admin/statistics_panel/store/mutations.js @@ -0,0 +1,16 @@ +import * as types from './mutation_types'; + +export default { + [types.REQUEST_STATISTICS](state) { + state.isLoading = true; + }, + [types.RECEIVE_STATISTICS_SUCCESS](state, data) { + state.isLoading = false; + state.error = null; + state.statistics = data; + }, + [types.RECEIVE_STATISTICS_ERROR](state, error) { + state.isLoading = false; + state.error = error; + }, +}; diff --git a/app/assets/javascripts/admin/statistics_panel/store/state.js b/app/assets/javascripts/admin/statistics_panel/store/state.js new file mode 100644 index 00000000000..f2f2dc0a4d2 --- /dev/null +++ b/app/assets/javascripts/admin/statistics_panel/store/state.js @@ -0,0 +1,5 @@ +export default () => ({ + error: null, + isLoading: false, + statistics: null, +}); diff --git a/app/assets/javascripts/api.js b/app/assets/javascripts/api.js index 136ffdf8b9d..1d97ad5ec11 100644 --- a/app/assets/javascripts/api.js +++ b/app/assets/javascripts/api.js @@ -36,6 +36,7 @@ const Api = { branchSinglePath: '/api/:version/projects/:id/repository/branches/:branch', createBranchPath: '/api/:version/projects/:id/repository/branches', releasesPath: '/api/:version/projects/:id/releases', + adminStatisticsPath: 'api/:version/application/statistics', group(groupId, callback) { const url = Api.buildUrl(Api.groupPath).replace(':id', groupId); @@ -376,6 +377,11 @@ const Api = { return axios.get(url); }, + adminStatistics() { + const url = Api.buildUrl(this.adminStatisticsPath); + return axios.get(url); + }, + buildUrl(url) { return joinPaths(gon.relative_url_root || '', url.replace(':version', gon.api_version)); }, diff --git a/app/assets/javascripts/behaviors/shortcuts/shortcuts.js b/app/assets/javascripts/behaviors/shortcuts/shortcuts.js index 7e3515b1f4b..66cb9fd7672 100644 --- a/app/assets/javascripts/behaviors/shortcuts/shortcuts.js +++ b/app/assets/javascripts/behaviors/shortcuts/shortcuts.js @@ -46,7 +46,6 @@ export default class Shortcuts { $(document).on('click.more_help', '.js-more-help-button', function clickMoreHelp(e) { $(this).remove(); - $('.hidden-shortcut').show(); e.preventDefault(); }); } @@ -104,7 +103,6 @@ export default class Shortcuts { return results; } - $('.hidden-shortcut').show(); return $('.js-more-help-button').remove(); }); } diff --git a/app/assets/javascripts/behaviors/shortcuts/shortcuts_issuable.js b/app/assets/javascripts/behaviors/shortcuts/shortcuts_issuable.js index c8eb96a625c..f7b327b2af1 100644 --- a/app/assets/javascripts/behaviors/shortcuts/shortcuts_issuable.js +++ b/app/assets/javascripts/behaviors/shortcuts/shortcuts_issuable.js @@ -6,7 +6,7 @@ import { CopyAsGFM } from '../markdown/copy_as_gfm'; import { getSelectedFragment } from '~/lib/utils/common_utils'; export default class ShortcutsIssuable extends Shortcuts { - constructor(isMergeRequest) { + constructor() { super(); Mousetrap.bind('a', () => ShortcutsIssuable.openSidebarDropdown('assignee')); @@ -14,12 +14,6 @@ export default class ShortcutsIssuable extends Shortcuts { Mousetrap.bind('l', () => ShortcutsIssuable.openSidebarDropdown('labels')); Mousetrap.bind('r', ShortcutsIssuable.replyWithSelectedText); Mousetrap.bind('e', ShortcutsIssuable.editIssue); - - if (isMergeRequest) { - this.enabledHelp.push('.hidden-shortcut.merge_requests'); - } else { - this.enabledHelp.push('.hidden-shortcut.issues'); - } } static replyWithSelectedText() { diff --git a/app/assets/javascripts/behaviors/shortcuts/shortcuts_navigation.js b/app/assets/javascripts/behaviors/shortcuts/shortcuts_navigation.js index bef1553703b..b46b4132ba8 100644 --- a/app/assets/javascripts/behaviors/shortcuts/shortcuts_navigation.js +++ b/app/assets/javascripts/behaviors/shortcuts/shortcuts_navigation.js @@ -23,7 +23,5 @@ export default class ShortcutsNavigation extends Shortcuts { Mousetrap.bind('g e', () => findAndFollowLink('.shortcuts-environments')); Mousetrap.bind('g l', () => findAndFollowLink('.shortcuts-metrics')); Mousetrap.bind('i', () => findAndFollowLink('.shortcuts-new-issue')); - - this.enabledHelp.push('.hidden-shortcut.project'); } } diff --git a/app/assets/javascripts/behaviors/shortcuts/shortcuts_network.js b/app/assets/javascripts/behaviors/shortcuts/shortcuts_network.js index a88c280fa3b..3e791e4673a 100644 --- a/app/assets/javascripts/behaviors/shortcuts/shortcuts_network.js +++ b/app/assets/javascripts/behaviors/shortcuts/shortcuts_network.js @@ -11,7 +11,5 @@ export default class ShortcutsNetwork extends ShortcutsNavigation { Mousetrap.bind(['down', 'j'], graph.scrollDown); Mousetrap.bind(['shift+up', 'shift+k'], graph.scrollTop); Mousetrap.bind(['shift+down', 'shift+j'], graph.scrollBottom); - - this.enabledHelp.push('.hidden-shortcut.network'); } } diff --git a/app/assets/javascripts/behaviors/shortcuts/shortcuts_wiki.js b/app/assets/javascripts/behaviors/shortcuts/shortcuts_wiki.js index 208c91a1f08..8b7e6a56d25 100644 --- a/app/assets/javascripts/behaviors/shortcuts/shortcuts_wiki.js +++ b/app/assets/javascripts/behaviors/shortcuts/shortcuts_wiki.js @@ -6,8 +6,6 @@ export default class ShortcutsWiki extends ShortcutsNavigation { constructor() { super(); Mousetrap.bind('e', ShortcutsWiki.editWiki); - - this.enabledHelp.push('.hidden-shortcut.wiki'); } static editWiki() { diff --git a/app/assets/javascripts/boards/components/board_new_issue.vue b/app/assets/javascripts/boards/components/board_new_issue.vue index f9284266b72..f9a08f151c5 100644 --- a/app/assets/javascripts/boards/components/board_new_issue.vue +++ b/app/assets/javascripts/boards/components/board_new_issue.vue @@ -2,9 +2,9 @@ import $ from 'jquery'; import { GlButton } from '@gitlab/ui'; import { getMilestone } from 'ee_else_ce/boards/boards_util'; +import ListIssue from 'ee_else_ce/boards/models/issue'; import eventHub from '../eventhub'; import ProjectSelect from './project_select.vue'; -import ListIssue from '../models/issue'; import boardsStore from '../stores/boards_store'; export default { @@ -54,6 +54,9 @@ export default { const assignees = this.list.assignee ? [this.list.assignee] : []; const milestone = getMilestone(this.list); + const { weightFeatureAvailable } = boardsStore; + const { weight } = weightFeatureAvailable ? boardsStore.state.currentBoard : {}; + const issue = new ListIssue({ title: this.title, labels, @@ -61,6 +64,7 @@ export default { assignees, milestone, project_id: this.selectedProject.id, + weight, }); eventHub.$emit(`scroll-board-list-${this.list.id}`); diff --git a/app/assets/javascripts/boards/components/boards_selector.vue b/app/assets/javascripts/boards/components/boards_selector.vue index 7296426549a..ebb2f5b23e4 100644 --- a/app/assets/javascripts/boards/components/boards_selector.vue +++ b/app/assets/javascripts/boards/components/boards_selector.vue @@ -245,6 +245,7 @@ export default { <div v-if="!loading" ref="content" + data-qa-selector="boards_dropdown_content" class="dropdown-content flex-fill" @scroll.passive="throttledSetScrollFade" > diff --git a/app/assets/javascripts/clusters/components/applications.vue b/app/assets/javascripts/clusters/components/applications.vue index b6da572b201..27959898fb7 100644 --- a/app/assets/javascripts/clusters/components/applications.vue +++ b/app/assets/javascripts/clusters/components/applications.vue @@ -458,7 +458,6 @@ export default { </div> </application-row> <application-row - v-if="isProjectCluster" id="knative" :logo-url="knativeLogo" :title="applications.knative.title" diff --git a/app/assets/javascripts/pages/admin/index.js b/app/assets/javascripts/pages/admin/index.js index 8a32556f06c..74f2eead755 100644 --- a/app/assets/javascripts/pages/admin/index.js +++ b/app/assets/javascripts/pages/admin/index.js @@ -1,3 +1,8 @@ import initAdmin from './admin'; +import initAdminStatisticsPanel from '../../admin/statistics_panel/index'; -document.addEventListener('DOMContentLoaded', initAdmin()); +document.addEventListener('DOMContentLoaded', () => { + const statisticsPanelContainer = document.getElementById('js-admin-statistics-container'); + initAdmin(); + initAdminStatisticsPanel(statisticsPanelContainer); +}); diff --git a/app/assets/javascripts/pipelines/components/graph/graph_component.vue b/app/assets/javascripts/pipelines/components/graph/graph_component.vue index ba0dea626dc..27c1b639889 100644 --- a/app/assets/javascripts/pipelines/components/graph/graph_component.vue +++ b/app/assets/javascripts/pipelines/components/graph/graph_component.vue @@ -20,6 +20,9 @@ export default { <stage-column-component v-for="(stage, index) in graph" :key="stage.name" + :class="{ + 'append-right-48': shouldAddRightMargin(index), + }" :title="capitalizeStageName(stage.name)" :groups="stage.groups" :stage-connector-class="stageConnectorClass(index, stage)" diff --git a/app/assets/javascripts/pipelines/mixins/graph_component_mixin.js b/app/assets/javascripts/pipelines/mixins/graph_component_mixin.js index 66e9476dadf..f383a4b3368 100644 --- a/app/assets/javascripts/pipelines/mixins/graph_component_mixin.js +++ b/app/assets/javascripts/pipelines/mixins/graph_component_mixin.js @@ -40,5 +40,15 @@ export default { refreshPipelineGraph() { this.$emit('refreshPipelineGraph'); }, + /** + * CSS class is applied: + * - if pipeline graph contains only one stage column component + * + * @param {number} index + * @returns {boolean} + */ + shouldAddRightMargin(index) { + return !(index === this.graph.length - 1); + }, }, }; diff --git a/app/assets/javascripts/vue_shared/components/gl_toggle_vuex.vue b/app/assets/javascripts/vue_shared/components/gl_toggle_vuex.vue new file mode 100644 index 00000000000..b649dac029a --- /dev/null +++ b/app/assets/javascripts/vue_shared/components/gl_toggle_vuex.vue @@ -0,0 +1,49 @@ +<script> +import { GlToggle } from '@gitlab/ui'; +import { capitalizeFirstCharacter } from '~/lib/utils/text_utility'; + +export default { + name: 'GlToggleVuex', + components: { + GlToggle, + }, + props: { + stateProperty: { + type: String, + required: true, + }, + storeModule: { + type: String, + required: false, + default: null, + }, + setAction: { + type: String, + required: false, + default() { + return `set${capitalizeFirstCharacter(this.stateProperty)}`; + }, + }, + }, + computed: { + value: { + get() { + const { state } = this.$store; + const { stateProperty, storeModule } = this; + return storeModule ? state[storeModule][stateProperty] : state[stateProperty]; + }, + set(value) { + const { stateProperty, storeModule, setAction } = this; + const action = storeModule ? `${storeModule}/${setAction}` : setAction; + this.$store.dispatch(action, { key: stateProperty, value }); + }, + }, + }, +}; +</script> + +<template> + <gl-toggle v-model="value"> + <slot v-bind="{ value }"></slot> + </gl-toggle> +</template> |