diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2019-12-13 21:08:06 +0300 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2019-12-13 21:08:06 +0300 |
commit | 7cc6c10c68915f5019ab8c2029eeb462c8fed4ef (patch) | |
tree | 419e5fee5bb60e71bef076157627812d54e142bc /app | |
parent | 630101f7f93847f39a4d2f87d92f514c973cdc1e (diff) |
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'app')
18 files changed, 186 insertions, 62 deletions
diff --git a/app/assets/javascripts/error_tracking/components/error_tracking_list.vue b/app/assets/javascripts/error_tracking/components/error_tracking_list.vue index 5cd68687329..23e251e4201 100644 --- a/app/assets/javascripts/error_tracking/components/error_tracking_list.vue +++ b/app/assets/javascripts/error_tracking/components/error_tracking_list.vue @@ -17,8 +17,6 @@ import AccessorUtils from '~/lib/utils/accessor'; import Icon from '~/vue_shared/components/icon.vue'; import TimeAgo from '~/vue_shared/components/time_ago_tooltip.vue'; import { __ } from '~/locale'; -import TrackEventDirective from '~/vue_shared/directives/track_event'; -import { trackViewInSentryOptions } from '../utils'; export default { fields: [ @@ -27,6 +25,11 @@ export default { { key: 'users', label: __('Users') }, { key: 'lastSeen', label: __('Last seen'), thClass: 'w-15p' }, ], + sortFields: { + last_seen: __('Last Seen'), + first_seen: __('First Seen'), + frequency: __('Frequency'), + }, components: { GlEmptyState, GlButton, @@ -43,7 +46,6 @@ export default { }, directives: { GlTooltip: GlTooltipDirective, - TrackEvent: TrackEventDirective, }, props: { indexPath: { @@ -74,45 +76,47 @@ export default { }; }, computed: { - ...mapState('list', ['errors', 'externalUrl', 'loading', 'recentSearches']), + ...mapState('list', ['errors', 'loading', 'searchQuery', 'sortField', 'recentSearches']), }, created() { if (this.errorTrackingEnabled) { - this.startPolling(this.indexPath); + this.setEndpoint(this.indexPath); + this.startPolling(); } }, methods: { ...mapActions('list', [ 'startPolling', 'restartPolling', + 'setEndpoint', + 'searchByQuery', + 'sortByField', 'addRecentSearch', 'clearRecentSearches', 'loadRecentSearches', 'setIndexPath', ]), - filterErrors() { - const searchTerm = this.errorSearchQuery.trim(); - this.addRecentSearch(searchTerm); - - this.startPolling(`${this.indexPath}?search_term=${searchTerm}`); - }, setSearchText(text) { this.errorSearchQuery = text; - this.filterErrors(); + this.searchByQuery(text); }, - trackViewInSentryOptions, getDetailsLink(errorId) { return `error_tracking/${errorId}/details`; }, + isCurrentSortField(field) { + return field === this.sortField; + }, }, }; </script> <template> - <div> + <div class="error-list"> <div v-if="errorTrackingEnabled"> - <div class="d-flex flex-row justify-content-around bg-secondary border p-3"> - <div class="filtered-search-box"> + <div + class="d-flex flex-row justify-content-around align-items-center bg-secondary border mt-2" + > + <div class="filtered-search-box flex-grow-1 my-3 ml-3 mr-2"> <gl-dropdown :text="__('Recent searches')" class="filtered-search-history-dropdown-wrapper d-none d-md-block" @@ -143,7 +147,7 @@ export default { :disabled="loading" :placeholder="__('Search or filter results…')" autofocus - @keyup.enter.native="filterErrors" + @keyup.enter.native="searchByQuery(errorSearchQuery)" /> </div> <div class="gl-search-box-by-type-right-icons"> @@ -160,16 +164,28 @@ export default { </div> </div> - <gl-button - v-track-event="trackViewInSentryOptions(externalUrl)" - class="ml-3" - variant="primary" - :href="externalUrl" - target="_blank" + <gl-dropdown + :text="$options.sortFields[sortField]" + left + :disabled="loading" + class="mr-3" + menu-class="sort-dropdown" > - {{ __('View in Sentry') }} - <icon name="external-link" class="flex-shrink-0" /> - </gl-button> + <gl-dropdown-item + v-for="(label, field) in $options.sortFields" + :key="field" + @click="sortByField(field)" + > + <span class="d-flex"> + <icon + class="flex-shrink-0 append-right-4" + :class="{ invisible: !isCurrentSortField(field) }" + name="mobile-issue-close" + /> + {{ label }} + </span> + </gl-dropdown-item> + </gl-dropdown> </div> <div v-if="loading" class="py-3"> diff --git a/app/assets/javascripts/error_tracking/services/index.js b/app/assets/javascripts/error_tracking/services/index.js index 68988296cc2..3b3f8311d67 100644 --- a/app/assets/javascripts/error_tracking/services/index.js +++ b/app/assets/javascripts/error_tracking/services/index.js @@ -1,7 +1,7 @@ import axios from '~/lib/utils/axios_utils'; export default { - getSentryData({ endpoint }) { - return axios.get(endpoint); + getSentryData({ endpoint, params }) { + return axios.get(endpoint, { params }); }, }; diff --git a/app/assets/javascripts/error_tracking/store/list/actions.js b/app/assets/javascripts/error_tracking/store/list/actions.js index 13b15549d81..b1c81b55e58 100644 --- a/app/assets/javascripts/error_tracking/store/list/actions.js +++ b/app/assets/javascripts/error_tracking/store/list/actions.js @@ -6,19 +6,24 @@ import { __, sprintf } from '~/locale'; let eTagPoll; -export function startPolling({ commit, dispatch }, endpoint) { +export function startPolling({ state, commit, dispatch }) { commit(types.SET_LOADING, true); eTagPoll = new Poll({ resource: Service, method: 'getSentryData', - data: { endpoint }, + data: { + endpoint: state.endpoint, + params: { + search_term: state.searchQuery, + sort: state.sortField, + }, + }, successCallback: ({ data }) => { if (!data) { return; } commit(types.SET_ERRORS, data.errors); - commit(types.SET_EXTERNAL_URL, data.external_url); commit(types.SET_LOADING, false); dispatch('stopPolling'); }, @@ -45,7 +50,6 @@ export const stopPolling = () => { export function restartPolling({ commit }) { commit(types.SET_ERRORS, []); - commit(types.SET_EXTERNAL_URL, ''); commit(types.SET_LOADING, true); if (eTagPoll) eTagPoll.restart(); @@ -67,4 +71,22 @@ export function clearRecentSearches({ commit }) { commit(types.CLEAR_RECENT_SEARCHES); } +export const searchByQuery = ({ commit, dispatch }, query) => { + const searchQuery = query.trim(); + commit(types.SET_SEARCH_QUERY, searchQuery); + commit(types.ADD_RECENT_SEARCH, searchQuery); + dispatch('stopPolling'); + dispatch('startPolling'); +}; + +export const sortByField = ({ commit, dispatch }, field) => { + commit(types.SET_SORT_FIELD, field); + dispatch('stopPolling'); + dispatch('startPolling'); +}; + +export const setEndpoint = ({ commit }, endpoint) => { + commit(types.SET_ENDPOINT, endpoint); +}; + export default () => {}; diff --git a/app/assets/javascripts/error_tracking/store/list/mutation_types.js b/app/assets/javascripts/error_tracking/store/list/mutation_types.js index 4199e8d5cda..3ebfef76324 100644 --- a/app/assets/javascripts/error_tracking/store/list/mutation_types.js +++ b/app/assets/javascripts/error_tracking/store/list/mutation_types.js @@ -1,7 +1,9 @@ export const SET_ERRORS = 'SET_ERRORS'; -export const SET_EXTERNAL_URL = 'SET_EXTERNAL_URL'; export const SET_INDEX_PATH = 'SET_INDEX_PATH'; export const SET_LOADING = 'SET_LOADING'; export const ADD_RECENT_SEARCH = 'ADD_RECENT_SEARCH'; export const CLEAR_RECENT_SEARCHES = 'CLEAR_RECENT_SEARCHES'; export const LOAD_RECENT_SEARCHES = 'LOAD_RECENT_SEARCHES'; +export const SET_ENDPOINT = 'SET_ENDPOINT'; +export const SET_SORT_FIELD = 'SET_SORT_FIELD'; +export const SET_SEARCH_QUERY = 'SET_SEARCH_QUERY'; diff --git a/app/assets/javascripts/error_tracking/store/list/mutations.js b/app/assets/javascripts/error_tracking/store/list/mutations.js index 18404d3b0af..048660eaeeb 100644 --- a/app/assets/javascripts/error_tracking/store/list/mutations.js +++ b/app/assets/javascripts/error_tracking/store/list/mutations.js @@ -6,9 +6,6 @@ export default { [types.SET_ERRORS](state, data) { state.errors = convertObjectPropsToCamelCase(data, { deep: true }); }, - [types.SET_EXTERNAL_URL](state, url) { - state.externalUrl = url; - }, [types.SET_LOADING](state, loading) { state.loading = loading; }, @@ -47,4 +44,13 @@ export default { throw e; } }, + [types.SET_SORT_FIELD](state, field) { + state.sortField = field; + }, + [types.SET_SEARCH_QUERY](state, query) { + state.searchQuery = query; + }, + [types.SET_ENDPOINT](state, endpoint) { + state.endpoint = endpoint; + }, }; diff --git a/app/assets/javascripts/error_tracking/store/list/state.js b/app/assets/javascripts/error_tracking/store/list/state.js index f1f0369e5f3..f20b707142e 100644 --- a/app/assets/javascripts/error_tracking/store/list/state.js +++ b/app/assets/javascripts/error_tracking/store/list/state.js @@ -1,7 +1,9 @@ export default () => ({ errors: [], - externalUrl: '', loading: true, + endpoint: null, + sortField: 'last_seen', + searchQuery: null, indexPath: '', recentSearches: [], }); diff --git a/app/assets/javascripts/error_tracking/utils.js b/app/assets/javascripts/error_tracking/utils.js index b832b1371b1..3c382ccd1aa 100644 --- a/app/assets/javascripts/error_tracking/utils.js +++ b/app/assets/javascripts/error_tracking/utils.js @@ -1,15 +1,4 @@ -/* eslint-disable @gitlab/i18n/no-non-i18n-strings */ - -/** - * Tracks snowplow event when user clicks View in Sentry btn - * @param {String} externalUrl that will be send as a property for the event - */ -export const trackViewInSentryOptions = url => ({ - category: 'Error Tracking', - action: 'click_view_in_sentry', - label: 'External Url', - property: url, -}); +/* eslint-disable @gitlab/i18n/no-non-i18n-strings, import/prefer-default-export */ /** * Tracks snowplow event when User clicks on error link to Sentry diff --git a/app/assets/javascripts/pages/snippets/show/index.js b/app/assets/javascripts/pages/snippets/show/index.js index 5efcc901fde..6e00c14f43e 100644 --- a/app/assets/javascripts/pages/snippets/show/index.js +++ b/app/assets/javascripts/pages/snippets/show/index.js @@ -3,6 +3,7 @@ import BlobViewer from '~/blob/viewer'; import ZenMode from '~/zen_mode'; import initNotes from '~/init_notes'; import snippetEmbed from '~/snippet/snippet_embed'; +import initSnippetsApp from '~/snippets'; document.addEventListener('DOMContentLoaded', () => { if (!gon.features.snippetsVue) { @@ -11,5 +12,7 @@ document.addEventListener('DOMContentLoaded', () => { initNotes(); new ZenMode(); // eslint-disable-line no-new snippetEmbed(); + } else { + initSnippetsApp(); } }); diff --git a/app/assets/javascripts/projects/project_new.js b/app/assets/javascripts/projects/project_new.js index dac105d7243..92c4c05bd87 100644 --- a/app/assets/javascripts/projects/project_new.js +++ b/app/assets/javascripts/projects/project_new.js @@ -128,15 +128,15 @@ const bindEvents = () => { }, iosswift: { text: s__('ProjectTemplates|iOS (Swift)'), - icon: '.template-option svg.icon-gitlab', + icon: '.template-option .icon-iosswift', }, dotnetcore: { text: s__('ProjectTemplates|.NET Core'), - icon: '.template-option .icon-dotnet', + icon: '.template-option .icon-dotnetcore', }, android: { text: s__('ProjectTemplates|Android'), - icon: '.template-option svg.icon-android', + icon: '.template-option .icon-android', }, gomicro: { text: s__('ProjectTemplates|Go Micro'), @@ -164,27 +164,27 @@ const bindEvents = () => { }, nfhugo: { text: s__('ProjectTemplates|Netlify/Hugo'), - icon: '.template-option .icon-netlify', + icon: '.template-option .icon-nfhugo', }, nfjekyll: { text: s__('ProjectTemplates|Netlify/Jekyll'), - icon: '.template-option .icon-netlify', + icon: '.template-option .icon-nfjekyll', }, nfplainhtml: { text: s__('ProjectTemplates|Netlify/Plain HTML'), - icon: '.template-option .icon-netlify', + icon: '.template-option .icon-nfplainhtml', }, nfgitbook: { text: s__('ProjectTemplates|Netlify/GitBook'), - icon: '.template-option .icon-netlify', + icon: '.template-option .icon-nfgitbook', }, nfhexo: { text: s__('ProjectTemplates|Netlify/Hexo'), - icon: '.template-option .icon-netlify', + icon: '.template-option .icon-nfhexo', }, salesforcedx: { text: s__('ProjectTemplates|SalesforceDX'), - icon: '.template-option svg.icon-gitlab', + icon: '.template-option .icon-salesforcedx', }, serverless_framework: { text: s__('ProjectTemplates|Serverless Framework/JS'), diff --git a/app/assets/javascripts/snippets/components/app.vue b/app/assets/javascripts/snippets/components/app.vue new file mode 100644 index 00000000000..e3d6cdd4606 --- /dev/null +++ b/app/assets/javascripts/snippets/components/app.vue @@ -0,0 +1,31 @@ +<script> +import getSnippet from '../queries/getSnippet.query.graphql'; + +export default { + apollo: { + snippetData: { + query: getSnippet, + variables() { + return { + ids: this.snippetGid, + }; + }, + update: data => data.snippets.edges[0].node, + }, + }, + props: { + snippetGid: { + type: String, + required: true, + }, + }, + data() { + return { + snippetData: {}, + }; + }, +}; +</script> +<template> + <div class="js-snippet-view"></div> +</template> diff --git a/app/assets/javascripts/snippets/index.js b/app/assets/javascripts/snippets/index.js new file mode 100644 index 00000000000..654856f8d14 --- /dev/null +++ b/app/assets/javascripts/snippets/index.js @@ -0,0 +1,34 @@ +import Vue from 'vue'; +import Translate from '~/vue_shared/translate'; +import VueApollo from 'vue-apollo'; +import createDefaultClient from '~/lib/graphql'; + +import SnippetsApp from './components/app.vue'; + +Vue.use(VueApollo); +Vue.use(Translate); + +export default () => { + const el = document.getElementById('js-snippet-view'); + + if (!el) { + return false; + } + + const { snippetGid } = el.dataset; + const apolloProvider = new VueApollo({ + defaultClient: createDefaultClient(), + }); + + return new Vue({ + el, + apolloProvider, + render(createElement) { + return createElement(SnippetsApp, { + props: { + snippetGid, + }, + }); + }, + }); +}; diff --git a/app/assets/javascripts/snippets/queries/getSnippet.query.graphql b/app/assets/javascripts/snippets/queries/getSnippet.query.graphql new file mode 100644 index 00000000000..5a5f0d05c5b --- /dev/null +++ b/app/assets/javascripts/snippets/queries/getSnippet.query.graphql @@ -0,0 +1,13 @@ +query getSnippet($ids: [ID!]) { + snippets(ids: $ids) { + edges { + node { + title + description + createdAt + updatedAt + visibility + } + } + } +} diff --git a/app/assets/stylesheets/pages/error_tracking_list.scss b/app/assets/stylesheets/pages/error_tracking_list.scss new file mode 100644 index 00000000000..cd1adb9a754 --- /dev/null +++ b/app/assets/stylesheets/pages/error_tracking_list.scss @@ -0,0 +1,5 @@ +.error-list { + .sort-dropdown { + min-width: auto; + } +} diff --git a/app/assets/stylesheets/pages/groups.scss b/app/assets/stylesheets/pages/groups.scss index 1502cf18440..1cf72c51ca7 100644 --- a/app/assets/stylesheets/pages/groups.scss +++ b/app/assets/stylesheets/pages/groups.scss @@ -25,6 +25,7 @@ .description p { margin-bottom: 0; + color: $gl-text-color-secondary; } } diff --git a/app/views/admin/groups/_group.html.haml b/app/views/admin/groups/_group.html.haml index 395c469255e..3444e423235 100644 --- a/app/views/admin/groups/_group.html.haml +++ b/app/views/admin/groups/_group.html.haml @@ -1,7 +1,7 @@ - group = local_assigns.fetch(:group) - css_class = 'no-description' if group.description.blank? -%li.group-row{ class: css_class } +%li.group-row.py-3{ class: css_class } .controls = link_to _('Edit'), admin_group_edit_path(group), id: "edit_#{dom_id(group)}", class: 'btn' = link_to _('Delete'), [:admin, group], data: { confirm: _("Are you sure you want to remove %{group_name}?") % { group_name: group.name } }, method: :delete, class: 'btn btn-remove' diff --git a/app/views/shared/groups/_group.html.haml b/app/views/shared/groups/_group.html.haml index 609b8dce21a..e47967ef622 100644 --- a/app/views/shared/groups/_group.html.haml +++ b/app/views/shared/groups/_group.html.haml @@ -1,7 +1,7 @@ - user = local_assigns.fetch(:user, current_user) - access = user&.max_member_access_for_group(group.id) -%li.group-row{ class: ('no-description' if group.description.blank?) } +%li.group-row.py-3{ class: ('no-description' if group.description.blank?) } .stats %span = icon('bookmark') diff --git a/app/views/shared/snippets/_snippet.html.haml b/app/views/shared/snippets/_snippet.html.haml index f38e30b0c55..9e038854c59 100644 --- a/app/views/shared/snippets/_snippet.html.haml +++ b/app/views/shared/snippets/_snippet.html.haml @@ -1,7 +1,7 @@ - link_project = local_assigns.fetch(:link_project, false) - notes_count = @noteable_meta_data[snippet.id].user_notes_count -%li.snippet-row +%li.snippet-row.py-3 = image_tag avatar_icon_for_user(snippet.author), class: "avatar s40 d-none d-sm-block", alt: '' .title diff --git a/app/views/snippets/show.html.haml b/app/views/snippets/show.html.haml index c77b05e3ea8..080c0ab6ece 100644 --- a/app/views/snippets/show.html.haml +++ b/app/views/snippets/show.html.haml @@ -5,7 +5,7 @@ - page_title "#{@snippet.title} (#{@snippet.to_reference})", _("Snippets") - if Feature.enabled?(:snippets_vue) - #js-snippet-view{ 'data-qa-selector': 'snippet_view' } + #js-snippet-view{ data: {'qa-selector': 'snippet_view', 'snippet-gid': @snippet.to_global_id} } - else = render 'shared/snippets/header' |