Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
parent
0923a94d58
commit
9a5dcad39c
|
@ -1 +1 @@
|
|||
2.6.0
|
||||
2.7.0
|
||||
|
|
|
@ -1,11 +1,9 @@
|
|||
<script>
|
||||
import { mapGetters, mapActions } from 'vuex';
|
||||
import Sortable from 'sortablejs';
|
||||
import isWipLimitsOn from 'ee_else_ce/boards/mixins/is_wip_limits';
|
||||
import BoardListHeader from 'ee_else_ce/boards/components/board_list_header.vue';
|
||||
import Tooltip from '~/vue_shared/directives/tooltip';
|
||||
import EmptyComponent from '~/vue_shared/components/empty_component';
|
||||
import glFeatureFlagMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
|
||||
import BoardBlankState from './board_blank_state.vue';
|
||||
import BoardList from './board_list.vue';
|
||||
import boardsStore from '../stores/boards_store';
|
||||
|
@ -23,7 +21,7 @@ export default {
|
|||
directives: {
|
||||
Tooltip,
|
||||
},
|
||||
mixins: [isWipLimitsOn, glFeatureFlagMixin()],
|
||||
mixins: [isWipLimitsOn],
|
||||
props: {
|
||||
list: {
|
||||
type: Object,
|
||||
|
@ -64,7 +62,6 @@ export default {
|
|||
};
|
||||
},
|
||||
computed: {
|
||||
...mapGetters(['getIssues']),
|
||||
showBoardListAndBoardInfo() {
|
||||
return this.list.type !== ListType.blank && this.list.type !== ListType.promotion;
|
||||
},
|
||||
|
@ -72,36 +69,19 @@ export default {
|
|||
// eslint-disable-next-line @gitlab/require-i18n-strings
|
||||
return `boards.${this.boardId}.${this.list.type}.${this.list.id}`;
|
||||
},
|
||||
listIssues() {
|
||||
if (!this.glFeatures.graphqlBoardLists) {
|
||||
return this.list.issues;
|
||||
}
|
||||
return this.getIssues(this.list.id);
|
||||
},
|
||||
shouldFetchIssues() {
|
||||
return this.glFeatures.graphqlBoardLists && this.list.type !== ListType.blank;
|
||||
},
|
||||
},
|
||||
watch: {
|
||||
filter: {
|
||||
handler() {
|
||||
if (this.shouldFetchIssues) {
|
||||
this.fetchIssuesForList(this.list.id);
|
||||
} else {
|
||||
this.list.page = 1;
|
||||
this.list.getIssues(true).catch(() => {
|
||||
// TODO: handle request error
|
||||
});
|
||||
}
|
||||
this.list.page = 1;
|
||||
this.list.getIssues(true).catch(() => {
|
||||
// TODO: handle request error
|
||||
});
|
||||
},
|
||||
deep: true,
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
if (this.shouldFetchIssues) {
|
||||
this.fetchIssuesForList(this.list.id);
|
||||
}
|
||||
|
||||
const instance = this;
|
||||
|
||||
const sortableOptions = getBoardSortableDefaultOptions({
|
||||
|
@ -128,7 +108,6 @@ export default {
|
|||
Sortable.create(this.$el.parentNode, sortableOptions);
|
||||
},
|
||||
methods: {
|
||||
...mapActions(['fetchIssuesForList']),
|
||||
showListNewIssueForm(listId) {
|
||||
eventHub.$emit('showForm', listId);
|
||||
},
|
||||
|
@ -163,7 +142,7 @@ export default {
|
|||
:disabled="disabled"
|
||||
:group-id="groupId || null"
|
||||
:issue-link-base="issueLinkBase"
|
||||
:issues="listIssues"
|
||||
:issues="list.issues"
|
||||
:list="list"
|
||||
:loading="list.loading"
|
||||
:root-path="rootPath"
|
||||
|
|
|
@ -6,7 +6,6 @@ import boardCard from './board_card.vue';
|
|||
import eventHub from '../eventhub';
|
||||
import boardsStore from '../stores/boards_store';
|
||||
import { sprintf, __ } from '~/locale';
|
||||
import glFeatureFlagMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
|
||||
import { deprecatedCreateFlash as createFlash } from '~/flash';
|
||||
import {
|
||||
getBoardSortableDefaultOptions,
|
||||
|
@ -25,7 +24,6 @@ export default {
|
|||
boardNewIssue,
|
||||
GlLoadingIcon,
|
||||
},
|
||||
mixins: [glFeatureFlagMixin()],
|
||||
props: {
|
||||
groupId: {
|
||||
type: Number,
|
||||
|
@ -85,7 +83,6 @@ export default {
|
|||
deep: true,
|
||||
},
|
||||
issues() {
|
||||
if (this.glFeatures.graphqlBoardLists) return;
|
||||
this.$nextTick(() => {
|
||||
if (
|
||||
this.scrollHeight() <= this.listHeight() &&
|
||||
|
@ -416,8 +413,6 @@ export default {
|
|||
this.showIssueForm = !this.showIssueForm;
|
||||
},
|
||||
onScroll() {
|
||||
if (this.glFeatures.graphqlBoardLists) return;
|
||||
|
||||
if (!this.list.loadingMore && this.scrollTop() > this.scrollHeight() - this.scrollOffset) {
|
||||
this.loadNextPage();
|
||||
}
|
||||
|
|
|
@ -129,9 +129,6 @@ export default {
|
|||
collapsedTooltipTitle() {
|
||||
return this.listTitle || this.listAssignee;
|
||||
},
|
||||
shouldDisplaySwimlanes() {
|
||||
return this.glFeatures.boardsWithSwimlanes && this.isSwimlanesOn;
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
...mapActions(['updateList']),
|
||||
|
@ -161,7 +158,7 @@ export default {
|
|||
}
|
||||
},
|
||||
updateListFunction() {
|
||||
if (this.shouldDisplaySwimlanes || this.glFeatures.graphqlBoardLists) {
|
||||
if (this.glFeatures.boardsWithSwimlanes && this.isSwimlanesHeader) {
|
||||
this.updateList({ listId: this.list.id, collapsed: !this.list.isExpanded });
|
||||
} else {
|
||||
this.list.update();
|
||||
|
@ -187,7 +184,7 @@ export default {
|
|||
<h3
|
||||
:class="{
|
||||
'user-can-drag': !disabled && !list.preset,
|
||||
'gl-py-3 gl-h-full': !list.isExpanded && !isSwimlanesHeader,
|
||||
'gl-py-3': !list.isExpanded && !isSwimlanesHeader,
|
||||
'gl-border-b-0': !list.isExpanded || isSwimlanesHeader,
|
||||
'gl-py-2': !list.isExpanded && isSwimlanesHeader,
|
||||
}"
|
||||
|
|
|
@ -42,9 +42,6 @@ export default {
|
|||
}
|
||||
return this.title === '';
|
||||
},
|
||||
shouldDisplaySwimlanes() {
|
||||
return this.glFeatures.boardsWithSwimlanes && this.isSwimlanesOn;
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
this.$refs.input.focus();
|
||||
|
@ -78,7 +75,7 @@ export default {
|
|||
eventHub.$emit(`scroll-board-list-${this.list.id}`);
|
||||
this.cancel();
|
||||
|
||||
if (this.shouldDisplaySwimlanes || this.glFeatures.graphqlBoardLists) {
|
||||
if (this.glFeatures.boardsWithSwimlanes && this.isSwimlanesOn) {
|
||||
this.addListIssue({ list: this.list, issue, position: 0 });
|
||||
}
|
||||
|
||||
|
@ -88,7 +85,7 @@ export default {
|
|||
// Need this because our jQuery very kindly disables buttons on ALL form submissions
|
||||
$(this.$refs.submitButton).enable();
|
||||
|
||||
if (!this.shouldDisplaySwimlanes && !this.glFeatures.graphqlBoardLists) {
|
||||
if (!this.glFeatures.boardsWithSwimlanes || !this.isSwimlanesOn) {
|
||||
boardsStore.setIssueDetail(issue);
|
||||
boardsStore.setListDetail(this.list);
|
||||
}
|
||||
|
@ -98,7 +95,7 @@ export default {
|
|||
$(this.$refs.submitButton).enable();
|
||||
|
||||
// Remove the issue
|
||||
if (this.shouldDisplaySwimlanes || this.glFeatures.graphqlBoardLists) {
|
||||
if (this.glFeatures.boardsWithSwimlanes && this.isSwimlanesOn) {
|
||||
this.addListIssueFailure({ list: this.list, issue });
|
||||
} else {
|
||||
this.list.removeIssue(issue);
|
||||
|
|
|
@ -27,7 +27,7 @@ export default class FilteredSearchBoards extends FilteredSearchManager {
|
|||
updateObject(path) {
|
||||
this.store.path = path.substr(1);
|
||||
|
||||
if (gon.features.boardsWithSwimlanes || gon.features.graphqlBoardLists) {
|
||||
if (gon.features.boardsWithSwimlanes) {
|
||||
boardsStore.updateFiltersUrl();
|
||||
boardsStore.performSearch();
|
||||
}
|
||||
|
|
|
@ -84,6 +84,10 @@ export default () => {
|
|||
},
|
||||
store,
|
||||
apolloProvider,
|
||||
provide: {
|
||||
// TODO: Mv all non-reactive props from data/props to here.
|
||||
rootPath: $boardApp.dataset.rootPath,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
state: boardsStore.state,
|
||||
|
|
|
@ -3,14 +3,13 @@
|
|||
query ListIssues(
|
||||
$fullPath: ID!
|
||||
$boardId: ID!
|
||||
$id: ID
|
||||
$filters: BoardIssueInput
|
||||
$isGroup: Boolean = false
|
||||
$isProject: Boolean = false
|
||||
) {
|
||||
group(fullPath: $fullPath) @include(if: $isGroup) {
|
||||
board(id: $boardId) {
|
||||
lists(id: $id) {
|
||||
lists {
|
||||
nodes {
|
||||
id
|
||||
issues(filters: $filters) {
|
||||
|
@ -24,7 +23,7 @@ query ListIssues(
|
|||
}
|
||||
project(fullPath: $fullPath) @include(if: $isProject) {
|
||||
board(id: $boardId) {
|
||||
lists(id: $id) {
|
||||
lists {
|
||||
nodes {
|
||||
id
|
||||
issues(filters: $filters) {
|
||||
|
|
|
@ -79,10 +79,10 @@ export default {
|
|||
lists = lists.nodes.map(list =>
|
||||
boardStore.updateListPosition({
|
||||
...list,
|
||||
doNotFetchIssues: true,
|
||||
id: getIdFromGraphQLId(list.id),
|
||||
}),
|
||||
);
|
||||
commit(types.RECEIVE_BOARD_LISTS_SUCCESS, sortBy(lists, 'position'));
|
||||
commit(types.RECEIVE_LISTS, sortBy(lists, 'position'));
|
||||
// Backlog list needs to be created if it doesn't exist
|
||||
if (!lists.find(l => l.type === ListType.backlog)) {
|
||||
dispatch('createList', { backlog: true });
|
||||
|
@ -113,7 +113,7 @@ export default {
|
|||
commit(types.CREATE_LIST_FAILURE);
|
||||
} else {
|
||||
const list = data.boardListCreate?.list;
|
||||
dispatch('addList', list);
|
||||
dispatch('addList', { ...list, id: getIdFromGraphQLId(list.id) });
|
||||
}
|
||||
})
|
||||
.catch(() => {
|
||||
|
@ -124,8 +124,8 @@ export default {
|
|||
addList: ({ state, commit }, list) => {
|
||||
const lists = state.boardLists;
|
||||
// Temporarily using positioning logic from boardStore
|
||||
lists.push(boardStore.updateListPosition({ ...list, doNotFetchIssues: true }));
|
||||
commit(types.RECEIVE_BOARD_LISTS_SUCCESS, sortBy(lists, 'position'));
|
||||
lists.push(boardStore.updateListPosition(list));
|
||||
commit(types.RECEIVE_LISTS, sortBy(lists, 'position'));
|
||||
},
|
||||
|
||||
showWelcomeList: ({ state, dispatch }) => {
|
||||
|
@ -197,33 +197,8 @@ export default {
|
|||
notImplemented();
|
||||
},
|
||||
|
||||
fetchIssuesForList: ({ state, commit }, listId) => {
|
||||
const { endpoints, boardType, filterParams } = state;
|
||||
const { fullPath, boardId } = endpoints;
|
||||
|
||||
const variables = {
|
||||
fullPath,
|
||||
boardId: fullBoardId(boardId),
|
||||
id: listId,
|
||||
filters: filterParams,
|
||||
isGroup: boardType === BoardType.group,
|
||||
isProject: boardType === BoardType.project,
|
||||
};
|
||||
|
||||
return gqlClient
|
||||
.query({
|
||||
query: listsIssuesQuery,
|
||||
context: {
|
||||
isSingleRequest: true,
|
||||
},
|
||||
variables,
|
||||
})
|
||||
.then(({ data }) => {
|
||||
const { lists } = data[boardType]?.board;
|
||||
const listIssues = formatListIssues(lists);
|
||||
commit(types.RECEIVE_ISSUES_FOR_LIST_SUCCESS, { listIssues, listId });
|
||||
})
|
||||
.catch(() => commit(types.RECEIVE_ISSUES_FOR_LIST_FAILURE, listId));
|
||||
fetchIssuesForList: () => {
|
||||
notImplemented();
|
||||
},
|
||||
|
||||
fetchIssuesForAllLists: ({ state, commit }) => {
|
||||
|
|
|
@ -304,11 +304,7 @@ const boardsStore = {
|
|||
onNewListIssueResponse(list, issue, data) {
|
||||
issue.refreshData(data);
|
||||
|
||||
if (
|
||||
!gon.features.boardsWithSwimlanes &&
|
||||
!gon.features.graphqlBoardLists &&
|
||||
list.issues.length > 1
|
||||
) {
|
||||
if (!gon.features.boardsWithSwimlanes && list.issuesSize > 1) {
|
||||
const moveBeforeId = list.issues[1].id;
|
||||
this.moveIssue(issue.id, null, null, null, moveBeforeId);
|
||||
}
|
||||
|
@ -727,10 +723,6 @@ const boardsStore = {
|
|||
newListIssue(list, issue) {
|
||||
list.addIssue(issue, null, 0);
|
||||
list.issuesSize += 1;
|
||||
let listId = list.id;
|
||||
if (typeof listId === 'string') {
|
||||
listId = getIdFromGraphQLId(listId);
|
||||
}
|
||||
|
||||
return this.newIssue(list.id, issue)
|
||||
.then(res => res.data)
|
||||
|
|
|
@ -14,11 +14,6 @@ export default {
|
|||
return state.issues[id] || {};
|
||||
},
|
||||
|
||||
getIssues: (state, getters) => listId => {
|
||||
const listIssueIds = state.issuesByListId[listId] || [];
|
||||
return listIssueIds.map(id => getters.getIssueById(id));
|
||||
},
|
||||
|
||||
getActiveIssue: state => {
|
||||
return state.issues[state.activeId] || {};
|
||||
},
|
||||
|
|
|
@ -2,7 +2,7 @@ export const SET_INITIAL_BOARD_DATA = 'SET_INITIAL_BOARD_DATA';
|
|||
export const SET_FILTERS = 'SET_FILTERS';
|
||||
export const CREATE_LIST_SUCCESS = 'CREATE_LIST_SUCCESS';
|
||||
export const CREATE_LIST_FAILURE = 'CREATE_LIST_FAILURE';
|
||||
export const RECEIVE_BOARD_LISTS_SUCCESS = 'RECEIVE_BOARD_LISTS_SUCCESS';
|
||||
export const RECEIVE_LISTS = 'RECEIVE_LISTS';
|
||||
export const SHOW_PROMOTION_LIST = 'SHOW_PROMOTION_LIST';
|
||||
export const REQUEST_ADD_LIST = 'REQUEST_ADD_LIST';
|
||||
export const RECEIVE_ADD_LIST_SUCCESS = 'RECEIVE_ADD_LIST_SUCCESS';
|
||||
|
@ -13,8 +13,6 @@ export const REQUEST_REMOVE_LIST = 'REQUEST_REMOVE_LIST';
|
|||
export const RECEIVE_REMOVE_LIST_SUCCESS = 'RECEIVE_REMOVE_LIST_SUCCESS';
|
||||
export const RECEIVE_REMOVE_LIST_ERROR = 'RECEIVE_REMOVE_LIST_ERROR';
|
||||
export const REQUEST_ISSUES_FOR_ALL_LISTS = 'REQUEST_ISSUES_FOR_ALL_LISTS';
|
||||
export const RECEIVE_ISSUES_FOR_LIST_FAILURE = 'RECEIVE_ISSUES_FOR_LIST_FAILURE';
|
||||
export const RECEIVE_ISSUES_FOR_LIST_SUCCESS = 'RECEIVE_ISSUES_FOR_LIST_SUCCESS';
|
||||
export const RECEIVE_ISSUES_FOR_ALL_LISTS_SUCCESS = 'RECEIVE_ISSUES_FOR_ALL_LISTS_SUCCESS';
|
||||
export const RECEIVE_ISSUES_FOR_ALL_LISTS_FAILURE = 'RECEIVE_ISSUES_FOR_ALL_LISTS_FAILURE';
|
||||
export const REQUEST_ADD_ISSUE = 'REQUEST_ADD_ISSUE';
|
||||
|
|
|
@ -35,7 +35,7 @@ export default {
|
|||
state.showPromotion = showPromotion;
|
||||
},
|
||||
|
||||
[mutationTypes.RECEIVE_BOARD_LISTS_SUCCESS]: (state, lists) => {
|
||||
[mutationTypes.RECEIVE_LISTS]: (state, lists) => {
|
||||
state.boardLists = lists;
|
||||
},
|
||||
|
||||
|
@ -89,20 +89,6 @@ export default {
|
|||
notImplemented();
|
||||
},
|
||||
|
||||
[mutationTypes.RECEIVE_ISSUES_FOR_LIST_SUCCESS]: (state, { listIssues, listId }) => {
|
||||
const { listData, issues } = listIssues;
|
||||
Vue.set(state, 'issues', { ...state.issues, ...issues });
|
||||
Vue.set(state.issuesByListId, listId, listData[listId]);
|
||||
const listIndex = state.boardLists.findIndex(l => l.id === listId);
|
||||
Vue.set(state.boardLists[listIndex], 'loading', false);
|
||||
},
|
||||
|
||||
[mutationTypes.RECEIVE_ISSUES_FOR_LIST_FAILURE]: (state, listId) => {
|
||||
state.error = __('An error occurred while fetching the board issues. Please reload the page.');
|
||||
const listIndex = state.boardLists.findIndex(l => l.id === listId);
|
||||
Vue.set(state.boardLists[listIndex], 'loading', false);
|
||||
},
|
||||
|
||||
[mutationTypes.REQUEST_ISSUES_FOR_ALL_LISTS]: state => {
|
||||
state.isLoadingIssues = true;
|
||||
},
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
<script>
|
||||
import { GlNewDropdown, GlNewDropdownItem, GlSprintf } from '@gitlab/ui';
|
||||
import { GlDropdown, GlNewDropdownItem, GlSprintf } from '@gitlab/ui';
|
||||
import { __, sprintf } from '~/locale';
|
||||
import allVersionsMixin from '../../mixins/all_versions';
|
||||
import { findVersionId } from '../../utils/design_management_utils';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
GlNewDropdown,
|
||||
GlDropdown,
|
||||
GlNewDropdownItem,
|
||||
GlSprintf,
|
||||
},
|
||||
|
@ -63,7 +63,7 @@ export default {
|
|||
</script>
|
||||
|
||||
<template>
|
||||
<gl-new-dropdown :text="dropdownText" size="small">
|
||||
<gl-dropdown :text="dropdownText" size="small">
|
||||
<gl-new-dropdown-item
|
||||
v-for="(version, index) in allVersions"
|
||||
:key="version.id"
|
||||
|
@ -77,5 +77,5 @@ export default {
|
|||
</template>
|
||||
</gl-sprintf>
|
||||
</gl-new-dropdown-item>
|
||||
</gl-new-dropdown>
|
||||
</gl-dropdown>
|
||||
</template>
|
||||
|
|
|
@ -1,8 +1,7 @@
|
|||
<script>
|
||||
/* eslint-disable vue/no-v-html */
|
||||
import { mapActions, mapGetters, mapState } from 'vuex';
|
||||
import { escape } from 'lodash';
|
||||
import { GlLoadingIcon } from '@gitlab/ui';
|
||||
import { GlLoadingIcon, GlSafeHtmlDirective as SafeHtml } from '@gitlab/ui';
|
||||
import glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
|
||||
import { __, sprintf } from '~/locale';
|
||||
import { deprecatedCreateFlash as createFlash } from '~/flash';
|
||||
|
@ -18,6 +17,9 @@ export default {
|
|||
DiffContent,
|
||||
GlLoadingIcon,
|
||||
},
|
||||
directives: {
|
||||
SafeHtml,
|
||||
},
|
||||
mixins: [glFeatureFlagsMixin()],
|
||||
props: {
|
||||
file: {
|
||||
|
@ -182,7 +184,7 @@ export default {
|
|||
/>
|
||||
|
||||
<div v-if="forkMessageVisible" class="js-file-fork-suggestion-section file-fork-suggestion">
|
||||
<span class="file-fork-suggestion-note" v-html="forkMessage"></span>
|
||||
<span v-safe-html="forkMessage" class="file-fork-suggestion-note"></span>
|
||||
<a
|
||||
:href="file.fork_path"
|
||||
class="js-fork-suggestion-button btn btn-grouped btn-inverted btn-success"
|
||||
|
@ -200,7 +202,7 @@ export default {
|
|||
<template v-else>
|
||||
<div :id="`diff-content-${file.file_hash}`">
|
||||
<div v-if="errorMessage" class="diff-viewer">
|
||||
<div class="nothing-here-block" v-html="errorMessage"></div>
|
||||
<div v-safe-html="errorMessage" class="nothing-here-block"></div>
|
||||
</div>
|
||||
<template v-else>
|
||||
<div v-show="isCollapsed" class="nothing-here-block diff-collapsed">
|
||||
|
|
|
@ -47,6 +47,7 @@ export default {
|
|||
'emptyRepo',
|
||||
'currentTree',
|
||||
'editorTheme',
|
||||
'getUrlForPath',
|
||||
]),
|
||||
themeName() {
|
||||
return window.gon?.user_color_scheme;
|
||||
|
@ -71,7 +72,7 @@ export default {
|
|||
return returnValue;
|
||||
},
|
||||
openFile(file) {
|
||||
this.$router.push(`/project${file.url}`);
|
||||
this.$router.push(this.getUrlForPath(file.path));
|
||||
},
|
||||
createNewFile() {
|
||||
this.$refs.newModal.open(modalTypes.blob);
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
* This component is an iterative step towards refactoring and simplifying `vue_shared/components/file_row.vue`
|
||||
* https://gitlab.com/gitlab-org/gitlab/-/merge_requests/23720
|
||||
*/
|
||||
import { mapGetters } from 'vuex';
|
||||
import FileRow from '~/vue_shared/components/file_row.vue';
|
||||
import FileRowExtra from './file_row_extra.vue';
|
||||
|
||||
|
@ -23,6 +24,9 @@ export default {
|
|||
dropdownOpen: false,
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
...mapGetters(['getUrlForPath']),
|
||||
},
|
||||
methods: {
|
||||
toggleDropdown(val) {
|
||||
this.dropdownOpen = val;
|
||||
|
@ -32,7 +36,13 @@ export default {
|
|||
</script>
|
||||
|
||||
<template>
|
||||
<file-row :file="file" v-bind="$attrs" @mouseleave="toggleDropdown(false)" v-on="$listeners">
|
||||
<file-row
|
||||
:file="file"
|
||||
:file-url="getUrlForPath(file.path)"
|
||||
v-bind="$attrs"
|
||||
@mouseleave="toggleDropdown(false)"
|
||||
v-on="$listeners"
|
||||
>
|
||||
<file-row-extra :file="file" :dropdown-open="dropdownOpen" @toggle="toggleDropdown($event)" />
|
||||
</file-row>
|
||||
</template>
|
||||
|
|
|
@ -10,7 +10,7 @@ export default {
|
|||
EditorModeDropdown,
|
||||
},
|
||||
computed: {
|
||||
...mapGetters(['currentMergeRequest', 'activeFile']),
|
||||
...mapGetters(['currentMergeRequest', 'activeFile', 'getUrlForPath']),
|
||||
...mapState(['viewer', 'currentMergeRequestId']),
|
||||
showLatestChangesText() {
|
||||
return !this.currentMergeRequestId || this.viewer === viewerTypes.diff;
|
||||
|
@ -24,7 +24,7 @@ export default {
|
|||
},
|
||||
mounted() {
|
||||
if (this.activeFile && this.activeFile.pending && !this.activeFile.deleted) {
|
||||
this.$router.push(`/project${this.activeFile.url}`, () => {
|
||||
this.$router.push(this.getUrlForPath(this.activeFile.path), () => {
|
||||
this.updateViewer('editor');
|
||||
});
|
||||
} else if (this.activeFile && this.activeFile.deleted) {
|
||||
|
|
|
@ -15,13 +15,13 @@ export default {
|
|||
},
|
||||
computed: {
|
||||
...mapState(['currentBranchId']),
|
||||
...mapGetters(['currentProject', 'currentTree', 'activeFile']),
|
||||
...mapGetters(['currentProject', 'currentTree', 'activeFile', 'getUrlForPath']),
|
||||
},
|
||||
mounted() {
|
||||
if (!this.activeFile) return;
|
||||
|
||||
if (this.activeFile.pending && !this.activeFile.deleted) {
|
||||
this.$router.push(`/project${this.activeFile.url}`, () => {
|
||||
this.$router.push(this.getUrlForPath(this.activeFile.path), () => {
|
||||
this.updateViewer('editor');
|
||||
});
|
||||
} else if (this.activeFile.deleted) {
|
||||
|
|
|
@ -48,6 +48,7 @@ export default {
|
|||
'renderWhitespaceInCode',
|
||||
'editorTheme',
|
||||
'entries',
|
||||
'currentProjectId',
|
||||
]),
|
||||
...mapGetters([
|
||||
'currentMergeRequest',
|
||||
|
@ -379,7 +380,7 @@ export default {
|
|||
:path="file.rawPath || file.path"
|
||||
:file-path="file.path"
|
||||
:file-size="file.size"
|
||||
:project-path="file.projectId"
|
||||
:project-path="currentProjectId"
|
||||
:commit-sha="currentBranchCommit"
|
||||
:type="fileType"
|
||||
/>
|
||||
|
@ -390,7 +391,7 @@ export default {
|
|||
:new-sha="currentMergeRequest.sha"
|
||||
:old-path="file.mrChange.old_path"
|
||||
:old-sha="currentMergeRequest.baseCommitSha"
|
||||
:project-path="file.projectId"
|
||||
:project-path="currentProjectId"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
<script>
|
||||
import { mapActions } from 'vuex';
|
||||
import { mapActions, mapGetters } from 'vuex';
|
||||
import { GlIcon } from '@gitlab/ui';
|
||||
import { __, sprintf } from '~/locale';
|
||||
|
||||
|
@ -26,6 +26,7 @@ export default {
|
|||
};
|
||||
},
|
||||
computed: {
|
||||
...mapGetters(['getUrlForPath']),
|
||||
closeLabel() {
|
||||
if (this.fileHasChanged) {
|
||||
return sprintf(__(`%{tabname} changed`), { tabname: this.tab.name });
|
||||
|
@ -52,7 +53,7 @@ export default {
|
|||
if (tab.pending) {
|
||||
this.openPendingTab({ file: tab, keyPrefix: tab.staged ? 'staged' : 'unstaged' });
|
||||
} else {
|
||||
this.$router.push(`/project${tab.url}`);
|
||||
this.$router.push(this.getUrlForPath(tab.path));
|
||||
}
|
||||
},
|
||||
mouseOverTab() {
|
||||
|
@ -79,7 +80,7 @@ export default {
|
|||
@mouseover="mouseOverTab"
|
||||
@mouseout="mouseOutTab"
|
||||
>
|
||||
<div :title="tab.url" class="multi-file-tab">
|
||||
<div :title="getUrlForPath(tab.path)" class="multi-file-tab">
|
||||
<file-icon :file-name="tab.name" :size="16" />
|
||||
{{ tab.name }}
|
||||
<file-status-icon :file="tab" />
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
<script>
|
||||
import { mapActions } from 'vuex';
|
||||
import { mapActions, mapGetters } from 'vuex';
|
||||
import RepoTab from './repo_tab.vue';
|
||||
|
||||
export default {
|
||||
|
@ -20,6 +20,9 @@ export default {
|
|||
required: true,
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
...mapGetters(['getUrlForPath']),
|
||||
},
|
||||
methods: {
|
||||
...mapActions(['updateViewer', 'removePendingTab']),
|
||||
openFileViewer(viewer) {
|
||||
|
@ -27,7 +30,7 @@ export default {
|
|||
|
||||
if (this.activeFile.pending) {
|
||||
return this.removePendingTab(this.activeFile).then(() => {
|
||||
this.$router.push(`/project${this.activeFile.url}`);
|
||||
this.$router.push(this.getUrlForPath(this.activeFile.path));
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -15,8 +15,6 @@ export const splitParent = path => {
|
|||
*/
|
||||
export const decorateFiles = ({
|
||||
data,
|
||||
projectId,
|
||||
branchId,
|
||||
tempFile = false,
|
||||
content = '',
|
||||
binary = false,
|
||||
|
@ -41,12 +39,9 @@ export const decorateFiles = ({
|
|||
parentPath = parentFolder && parentFolder.path;
|
||||
|
||||
const tree = decorateData({
|
||||
projectId,
|
||||
branchId,
|
||||
id: path,
|
||||
name,
|
||||
path,
|
||||
url: `/${projectId}/tree/${branchId}/-/${path}/`,
|
||||
type: 'tree',
|
||||
tempFile,
|
||||
changed: tempFile,
|
||||
|
@ -77,12 +72,9 @@ export const decorateFiles = ({
|
|||
parentPath = fileFolder && fileFolder.path;
|
||||
|
||||
file = decorateData({
|
||||
projectId,
|
||||
branchId,
|
||||
id: path,
|
||||
name,
|
||||
path,
|
||||
url: `/${projectId}/blob/${branchId}/-/${path}`,
|
||||
type: 'blob',
|
||||
tempFile,
|
||||
changed: tempFile,
|
||||
|
|
|
@ -33,7 +33,7 @@ export default {
|
|||
})
|
||||
.then(({ data }) => data);
|
||||
},
|
||||
getBaseRawFileData(file, sha) {
|
||||
getBaseRawFileData(file, projectId, ref) {
|
||||
if (file.tempFile || file.baseRaw) return Promise.resolve(file.baseRaw);
|
||||
|
||||
// if files are renamed, their base path has changed
|
||||
|
@ -44,10 +44,10 @@ export default {
|
|||
.get(
|
||||
joinPaths(
|
||||
gon.relative_url_root || '/',
|
||||
file.projectId,
|
||||
projectId,
|
||||
'-',
|
||||
'raw',
|
||||
sha,
|
||||
ref,
|
||||
escapeFileUrl(filePath),
|
||||
),
|
||||
{
|
||||
|
|
|
@ -54,8 +54,6 @@ export const createTempEntry = (
|
|||
|
||||
const data = decorateFiles({
|
||||
data: [fullName],
|
||||
projectId: state.currentProjectId,
|
||||
branchId: state.currentBranchId,
|
||||
type,
|
||||
tempFile: true,
|
||||
content,
|
||||
|
@ -64,11 +62,7 @@ export const createTempEntry = (
|
|||
});
|
||||
const { file, parentPath } = data;
|
||||
|
||||
commit(types.CREATE_TMP_ENTRY, {
|
||||
data,
|
||||
projectId: state.currentProjectId,
|
||||
branchId: state.currentBranchId,
|
||||
});
|
||||
commit(types.CREATE_TMP_ENTRY, { data });
|
||||
|
||||
if (type === 'blob') {
|
||||
if (openFile) commit(types.TOGGLE_FILE_OPEN, file.path);
|
||||
|
@ -254,7 +248,7 @@ export const renameEntry = ({ dispatch, commit, state, getters }, { path, name,
|
|||
}
|
||||
|
||||
if (newEntry.opened) {
|
||||
dispatch('router/push', `/project${newEntry.url}`, { root: true });
|
||||
dispatch('router/push', getters.getUrlForPath(newEntry.path), { root: true });
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -6,7 +6,7 @@ import * as types from '../mutation_types';
|
|||
import { setPageTitleForFile } from '../utils';
|
||||
import { viewerTypes, stageKeys } from '../../constants';
|
||||
|
||||
export const closeFile = ({ commit, state, dispatch }, file) => {
|
||||
export const closeFile = ({ commit, state, dispatch, getters }, file) => {
|
||||
const { path } = file;
|
||||
const indexOfClosedFile = state.openFiles.findIndex(f => f.key === file.key);
|
||||
const fileWasActive = file.active;
|
||||
|
@ -29,10 +29,12 @@ export const closeFile = ({ commit, state, dispatch }, file) => {
|
|||
keyPrefix: nextFileToOpen.staged ? 'staged' : 'unstaged',
|
||||
});
|
||||
} else {
|
||||
dispatch('router/push', `/project${nextFileToOpen.url}`, { root: true });
|
||||
dispatch('router/push', getters.getUrlForPath(nextFileToOpen.path), { root: true });
|
||||
}
|
||||
} else if (!state.openFiles.length) {
|
||||
dispatch('router/push', `/project/${file.projectId}/tree/${file.branchId}/`, { root: true });
|
||||
dispatch('router/push', `/project/${state.currentProjectId}/tree/${state.currentBranchId}/`, {
|
||||
root: true,
|
||||
});
|
||||
}
|
||||
|
||||
eventHub.$emit(`editor.update.model.dispose.${file.key}`);
|
||||
|
@ -121,7 +123,7 @@ export const getRawFileData = ({ state, commit, dispatch, getters }, { path }) =
|
|||
const baseSha =
|
||||
(getters.currentMergeRequest && getters.currentMergeRequest.baseCommitSha) || '';
|
||||
|
||||
return service.getBaseRawFileData(file, baseSha).then(baseRaw => {
|
||||
return service.getBaseRawFileData(file, state.currentProjectId, baseSha).then(baseRaw => {
|
||||
commit(types.SET_FILE_BASE_RAW_DATA, {
|
||||
file,
|
||||
baseRaw,
|
||||
|
@ -218,7 +220,7 @@ export const discardFileChanges = ({ dispatch, state, commit, getters }, path) =
|
|||
if (!isDestructiveDiscard && file.path === getters.activeFile?.path) {
|
||||
dispatch('updateDelayViewerUpdated', true)
|
||||
.then(() => {
|
||||
dispatch('router/push', `/project${file.url}`, { root: true });
|
||||
dispatch('router/push', getters.getUrlForPath(file.path), { root: true });
|
||||
})
|
||||
.catch(e => {
|
||||
throw e;
|
||||
|
@ -274,7 +276,7 @@ export const openPendingTab = ({ commit, dispatch, getters, state }, { file, key
|
|||
|
||||
commit(types.ADD_PENDING_TAB, { file, keyPrefix });
|
||||
|
||||
dispatch('router/push', `/project/${file.projectId}/tree/${state.currentBranchId}/`, {
|
||||
dispatch('router/push', `/project/${state.currentProjectId}/tree/${state.currentBranchId}/`, {
|
||||
root: true,
|
||||
});
|
||||
|
||||
|
|
|
@ -61,11 +61,7 @@ export const getFiles = ({ state, commit, dispatch }, payload = {}) =>
|
|||
service
|
||||
.getFiles(selectedProject.path_with_namespace, ref)
|
||||
.then(({ data }) => {
|
||||
const { entries, treeList } = decorateFiles({
|
||||
data,
|
||||
projectId,
|
||||
branchId,
|
||||
});
|
||||
const { entries, treeList } = decorateFiles({ data });
|
||||
|
||||
commit(types.SET_ENTRIES, entries);
|
||||
|
||||
|
|
|
@ -174,3 +174,6 @@ export const getAvailableFileName = (state, getters) => path => {
|
|||
|
||||
return newPath;
|
||||
};
|
||||
|
||||
export const getUrlForPath = state => path =>
|
||||
`/project/${state.currentProjectId}/tree/${state.currentBranchId}/-/${path}/`;
|
||||
|
|
|
@ -7,7 +7,6 @@ import treeMutations from './mutations/tree';
|
|||
import branchMutations from './mutations/branch';
|
||||
import {
|
||||
sortTree,
|
||||
replaceFileUrl,
|
||||
swapInParentTreeWithSorting,
|
||||
updateFileCollections,
|
||||
removeFromParentTree,
|
||||
|
@ -49,7 +48,7 @@ export default {
|
|||
entries,
|
||||
});
|
||||
},
|
||||
[types.CREATE_TMP_ENTRY](state, { data, projectId, branchId }) {
|
||||
[types.CREATE_TMP_ENTRY](state, { data }) {
|
||||
Object.keys(data.entries).reduce((acc, key) => {
|
||||
const entry = data.entries[key];
|
||||
const foundEntry = state.entries[key];
|
||||
|
@ -72,13 +71,12 @@ export default {
|
|||
return acc.concat(key);
|
||||
}, []);
|
||||
|
||||
const foundEntry = state.trees[`${projectId}/${branchId}`].tree.find(
|
||||
e => e.path === data.treeList[0].path,
|
||||
);
|
||||
const currentTree = state.trees[`${state.currentProjectId}/${state.currentBranchId}`];
|
||||
const foundEntry = currentTree.tree.find(e => e.path === data.treeList[0].path);
|
||||
|
||||
if (!foundEntry) {
|
||||
Object.assign(state.trees[`${projectId}/${branchId}`], {
|
||||
tree: sortTree(state.trees[`${projectId}/${branchId}`].tree.concat(data.treeList)),
|
||||
Object.assign(currentTree, {
|
||||
tree: sortTree(currentTree.tree.concat(data.treeList)),
|
||||
});
|
||||
}
|
||||
},
|
||||
|
@ -139,7 +137,6 @@ export default {
|
|||
prevId: undefined,
|
||||
prevPath: undefined,
|
||||
prevName: undefined,
|
||||
prevUrl: undefined,
|
||||
prevKey: undefined,
|
||||
prevParentPath: undefined,
|
||||
});
|
||||
|
@ -195,9 +192,6 @@ export default {
|
|||
const oldEntry = state.entries[path];
|
||||
const newPath = parentPath ? `${parentPath}/${name}` : name;
|
||||
const isRevert = newPath === oldEntry.prevPath;
|
||||
|
||||
const newUrl = replaceFileUrl(oldEntry.url, oldEntry.path, newPath);
|
||||
|
||||
const newKey = oldEntry.key.replace(new RegExp(oldEntry.path, 'g'), newPath);
|
||||
|
||||
const baseProps = {
|
||||
|
@ -205,7 +199,6 @@ export default {
|
|||
name,
|
||||
id: newPath,
|
||||
path: newPath,
|
||||
url: newUrl,
|
||||
key: newKey,
|
||||
parentPath: parentPath || '',
|
||||
};
|
||||
|
@ -216,7 +209,6 @@ export default {
|
|||
prevId: undefined,
|
||||
prevPath: undefined,
|
||||
prevName: undefined,
|
||||
prevUrl: undefined,
|
||||
prevKey: undefined,
|
||||
prevParentPath: undefined,
|
||||
}
|
||||
|
@ -224,7 +216,6 @@ export default {
|
|||
prevId: oldEntry.prevId || oldEntry.id,
|
||||
prevPath: oldEntry.prevPath || oldEntry.path,
|
||||
prevName: oldEntry.prevName || oldEntry.name,
|
||||
prevUrl: oldEntry.prevUrl || oldEntry.url,
|
||||
prevKey: oldEntry.prevKey || oldEntry.key,
|
||||
prevParentPath: oldEntry.prevParentPath || oldEntry.parentPath,
|
||||
};
|
||||
|
|
|
@ -12,10 +12,7 @@ export const dataStructure = () => ({
|
|||
// it can also contain a prefix `pending-` for files opened in review mode
|
||||
key: '',
|
||||
type: '',
|
||||
projectId: '',
|
||||
branchId: '',
|
||||
name: '',
|
||||
url: '',
|
||||
path: '',
|
||||
tempFile: false,
|
||||
tree: [],
|
||||
|
@ -44,10 +41,7 @@ export const dataStructure = () => ({
|
|||
export const decorateData = entity => {
|
||||
const {
|
||||
id,
|
||||
projectId,
|
||||
branchId,
|
||||
type,
|
||||
url,
|
||||
name,
|
||||
path,
|
||||
content = '',
|
||||
|
@ -63,12 +57,9 @@ export const decorateData = entity => {
|
|||
|
||||
return Object.assign(dataStructure(), {
|
||||
id,
|
||||
projectId,
|
||||
branchId,
|
||||
key: `${name}-${type}-${id}`,
|
||||
type,
|
||||
name,
|
||||
url,
|
||||
path,
|
||||
tempFile,
|
||||
opened,
|
||||
|
@ -189,11 +180,6 @@ export const mergeTrees = (fromTree, toTree) => {
|
|||
return toTree;
|
||||
};
|
||||
|
||||
export const replaceFileUrl = (url, oldPath, newPath) => {
|
||||
// Add `/-/` so that we don't accidentally replace project path
|
||||
return url.replace(`/-/${oldPath}`, `/-/${newPath}`);
|
||||
};
|
||||
|
||||
export const swapInStateArray = (state, arr, key, entryPath) =>
|
||||
Object.assign(state, {
|
||||
[arr]: state[arr].map(f => (f.key === key ? state.entries[entryPath] : f)),
|
||||
|
|
|
@ -6,7 +6,7 @@ import {
|
|||
GlIcon,
|
||||
GlFormGroup,
|
||||
GlFormCheckbox,
|
||||
GlNewDropdown,
|
||||
GlDropdown,
|
||||
GlNewDropdownItem,
|
||||
} from '@gitlab/ui';
|
||||
import {
|
||||
|
@ -24,7 +24,7 @@ export default {
|
|||
GlFormGroup,
|
||||
GlIcon,
|
||||
GlFormCheckbox,
|
||||
GlNewDropdown,
|
||||
GlDropdown,
|
||||
GlNewDropdownItem,
|
||||
},
|
||||
inject: ['service', 'alertSettings'],
|
||||
|
@ -101,7 +101,7 @@ export default {
|
|||
<gl-icon name="question" :size="12" />
|
||||
</gl-link>
|
||||
</label>
|
||||
<gl-new-dropdown
|
||||
<gl-dropdown
|
||||
id="alert-integration-settings-issue-template"
|
||||
data-qa-selector="incident_templates_dropdown"
|
||||
:text="issueTemplateHeader"
|
||||
|
@ -117,7 +117,7 @@ export default {
|
|||
>
|
||||
{{ template.name }}
|
||||
</gl-new-dropdown-item>
|
||||
</gl-new-dropdown>
|
||||
</gl-dropdown>
|
||||
</gl-form-group>
|
||||
|
||||
<gl-form-group class="gl-pl-0 gl-mb-5">
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
<script>
|
||||
import { mapState } from 'vuex';
|
||||
import { GlNewDropdown, GlNewDropdownItem, GlLink } from '@gitlab/ui';
|
||||
import { GlDropdown, GlNewDropdownItem, GlLink } from '@gitlab/ui';
|
||||
import { s__ } from '~/locale';
|
||||
import { defaultIntegrationLevel, overrideDropdownDescriptions } from '../constants';
|
||||
|
||||
|
@ -19,7 +19,7 @@ export default {
|
|||
dropdownOptions,
|
||||
name: 'OverrideDropdown',
|
||||
components: {
|
||||
GlNewDropdown,
|
||||
GlDropdown,
|
||||
GlNewDropdownItem,
|
||||
GlLink,
|
||||
},
|
||||
|
@ -73,7 +73,7 @@ export default {
|
|||
}}</gl-link>
|
||||
</span>
|
||||
<input name="service[inherit_from_id]" :value="override ? '' : inheritFromId" type="hidden" />
|
||||
<gl-new-dropdown :text="selected.text">
|
||||
<gl-dropdown :text="selected.text">
|
||||
<gl-new-dropdown-item
|
||||
v-for="option in $options.dropdownOptions"
|
||||
:key="option.value"
|
||||
|
@ -81,6 +81,6 @@ export default {
|
|||
>
|
||||
{{ option.text }}
|
||||
</gl-new-dropdown-item>
|
||||
</gl-new-dropdown>
|
||||
</gl-dropdown>
|
||||
</div>
|
||||
</template>
|
||||
|
|
|
@ -66,6 +66,7 @@ export default class IssuableForm {
|
|||
gl.GfmAutoComplete && gl.GfmAutoComplete.dataSources,
|
||||
).setup();
|
||||
this.usersSelect = new UsersSelect();
|
||||
this.reviewersSelect = new UsersSelect(undefined, '.js-reviewer-search');
|
||||
this.zenMode = new ZenMode();
|
||||
|
||||
this.titleField = this.form.find('input[name*="[title]"]');
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
import { sanitize } from 'dompurify';
|
||||
import { convertObjectPropsToCamelCase } from '~/lib/utils/common_utils';
|
||||
import updateDescription from '../utils/update_description';
|
||||
|
||||
|
@ -27,8 +28,8 @@ export default class Store {
|
|||
const details =
|
||||
descriptionSection != null && descriptionSection.getElementsByTagName('details');
|
||||
|
||||
this.state.descriptionHtml = updateDescription(data.description, details);
|
||||
this.state.titleHtml = data.title;
|
||||
this.state.descriptionHtml = updateDescription(sanitize(data.description), details);
|
||||
this.state.titleHtml = sanitize(data.title);
|
||||
this.state.lock_version = data.lock_version;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,7 +1,12 @@
|
|||
import { sanitize } from 'dompurify';
|
||||
|
||||
// We currently load + parse the data from the issue app and related merge request
|
||||
let cachedParsedData;
|
||||
|
||||
export const parseIssuableData = () => {
|
||||
try {
|
||||
if (cachedParsedData) return cachedParsedData;
|
||||
|
||||
const initialDataEl = document.getElementById('js-issuable-app-initial-data');
|
||||
|
||||
const parsedData = JSON.parse(initialDataEl.textContent.replace(/"/g, '"'));
|
||||
|
@ -9,6 +14,8 @@ export const parseIssuableData = () => {
|
|||
parsedData.initialTitleHtml = sanitize(parsedData.initialTitleHtml);
|
||||
parsedData.initialDescriptionHtml = sanitize(parsedData.initialDescriptionHtml);
|
||||
|
||||
cachedParsedData = parsedData;
|
||||
|
||||
return parsedData;
|
||||
} catch (e) {
|
||||
console.error(e); // eslint-disable-line no-console
|
||||
|
|
|
@ -2,9 +2,9 @@
|
|||
import {
|
||||
GlAlert,
|
||||
GlButton,
|
||||
GlNewDropdown,
|
||||
GlDropdown,
|
||||
GlNewDropdownItem,
|
||||
GlNewDropdownText,
|
||||
GlDropdownText,
|
||||
GlFormGroup,
|
||||
GlFormSelect,
|
||||
GlIcon,
|
||||
|
@ -34,9 +34,9 @@ export default {
|
|||
components: {
|
||||
GlAlert,
|
||||
GlButton,
|
||||
GlNewDropdown,
|
||||
GlDropdown,
|
||||
GlNewDropdownItem,
|
||||
GlNewDropdownText,
|
||||
GlDropdownText,
|
||||
GlFormGroup,
|
||||
GlFormSelect,
|
||||
GlIcon,
|
||||
|
@ -293,7 +293,7 @@ export default {
|
|||
<gl-icon name="arrow-right" :aria-label="__('Will be mapped to')" />
|
||||
</template>
|
||||
<template #cell(gitlabUsername)="data">
|
||||
<gl-new-dropdown
|
||||
<gl-dropdown
|
||||
:text="data.value || $options.currentUsername"
|
||||
class="w-100"
|
||||
:aria-label="
|
||||
|
@ -314,10 +314,10 @@ export default {
|
|||
{{ user.username }} ({{ user.name }})
|
||||
</gl-new-dropdown-item>
|
||||
|
||||
<gl-new-dropdown-text v-show="shouldShowNoMatchesFoundText" class="text-secondary">
|
||||
<gl-dropdown-text v-show="shouldShowNoMatchesFoundText" class="text-secondary">
|
||||
{{ __('No matches found') }}
|
||||
</gl-new-dropdown-text>
|
||||
</gl-new-dropdown>
|
||||
</gl-dropdown-text>
|
||||
</gl-dropdown>
|
||||
</template>
|
||||
</gl-table>
|
||||
|
||||
|
|
|
@ -642,6 +642,16 @@ export const secondsToMilliseconds = seconds => seconds * 1000;
|
|||
*/
|
||||
export const secondsToDays = seconds => Math.round(seconds / 86400);
|
||||
|
||||
/**
|
||||
* Returns the date n days after the date provided
|
||||
*
|
||||
* @param {Date} date the initial date
|
||||
* @param {Number} numberOfDays number of days after
|
||||
* @return {Date} the date following the date provided
|
||||
*/
|
||||
export const nDaysAfter = (date, numberOfDays) =>
|
||||
new Date(newDate(date)).setDate(date.getDate() + numberOfDays);
|
||||
|
||||
/**
|
||||
* Returns the date after the date provided
|
||||
*
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
<script>
|
||||
import {
|
||||
GlNewDropdown,
|
||||
GlDropdown,
|
||||
GlNewDropdownDivider,
|
||||
GlNewDropdownHeader,
|
||||
GlNewDropdownItem,
|
||||
|
@ -17,7 +17,7 @@ const SEARCH_DEBOUNCE_MS = 250;
|
|||
|
||||
export default {
|
||||
components: {
|
||||
GlNewDropdown,
|
||||
GlDropdown,
|
||||
GlNewDropdownDivider,
|
||||
GlNewDropdownHeader,
|
||||
GlNewDropdownItem,
|
||||
|
@ -188,7 +188,7 @@ export default {
|
|||
</script>
|
||||
|
||||
<template>
|
||||
<gl-new-dropdown v-bind="$attrs" class="project-milestone-combobox" @shown="focusSearchBox">
|
||||
<gl-dropdown v-bind="$attrs" class="project-milestone-combobox" @shown="focusSearchBox">
|
||||
<template slot="button-content">
|
||||
<span ref="buttonText" class="flex-grow-1 ml-1 text-muted">{{
|
||||
selectedMilestonesLabel
|
||||
|
@ -246,5 +246,5 @@ export default {
|
|||
<gl-new-dropdown-item v-for="(item, idx) in extraLinks" :key="idx" :href="item.url">
|
||||
<span class="pl-4">{{ item.text }}</span>
|
||||
</gl-new-dropdown-item>
|
||||
</gl-new-dropdown>
|
||||
</gl-dropdown>
|
||||
</template>
|
||||
|
|
|
@ -7,7 +7,7 @@ import {
|
|||
GlButtonGroup,
|
||||
GlFormGroup,
|
||||
GlFormInput,
|
||||
GlNewDropdown as GlDropdown,
|
||||
GlDropdown,
|
||||
GlNewDropdownItem as GlDropdownItem,
|
||||
GlModal,
|
||||
GlTooltipDirective,
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
import { mapState, mapGetters, mapActions } from 'vuex';
|
||||
import {
|
||||
GlDeprecatedButton,
|
||||
GlNewDropdown,
|
||||
GlDropdown,
|
||||
GlNewDropdownDivider,
|
||||
GlNewDropdownItem,
|
||||
GlModal,
|
||||
|
@ -23,7 +23,7 @@ import { getAddMetricTrackingOptions } from '../utils';
|
|||
export default {
|
||||
components: {
|
||||
GlDeprecatedButton,
|
||||
GlNewDropdown,
|
||||
GlDropdown,
|
||||
GlNewDropdownDivider,
|
||||
GlNewDropdownItem,
|
||||
GlModal,
|
||||
|
@ -143,7 +143,7 @@ export default {
|
|||
as part of https://gitlab.com/gitlab-org/gitlab-ui/-/issues/936
|
||||
The variant will create a dropdown with an icon, no text and no caret
|
||||
-->
|
||||
<gl-new-dropdown
|
||||
<gl-dropdown
|
||||
v-gl-tooltip
|
||||
data-testid="actions-menu"
|
||||
data-qa-selector="actions_menu_dropdown"
|
||||
|
@ -287,5 +287,5 @@ export default {
|
|||
:project-path="projectPath"
|
||||
/>
|
||||
</template>
|
||||
</gl-new-dropdown>
|
||||
</gl-dropdown>
|
||||
</template>
|
||||
|
|
|
@ -3,7 +3,7 @@ import { debounce } from 'lodash';
|
|||
import { mapActions, mapState, mapGetters } from 'vuex';
|
||||
import {
|
||||
GlButton,
|
||||
GlNewDropdown,
|
||||
GlDropdown,
|
||||
GlLoadingIcon,
|
||||
GlNewDropdownItem,
|
||||
GlNewDropdownHeader,
|
||||
|
@ -28,7 +28,7 @@ export default {
|
|||
components: {
|
||||
GlIcon,
|
||||
GlButton,
|
||||
GlNewDropdown,
|
||||
GlDropdown,
|
||||
GlLoadingIcon,
|
||||
GlNewDropdownItem,
|
||||
GlNewDropdownHeader,
|
||||
|
@ -181,7 +181,7 @@ export default {
|
|||
<span aria-hidden="true" class="gl-pl-3 border-left gl-mb-3 d-none d-sm-block"></span>
|
||||
|
||||
<div class="mb-2 pr-2 d-flex d-sm-block">
|
||||
<gl-new-dropdown
|
||||
<gl-dropdown
|
||||
id="monitor-environments-dropdown"
|
||||
ref="monitorEnvironmentsDropdown"
|
||||
class="flex-grow-1"
|
||||
|
@ -214,7 +214,7 @@ export default {
|
|||
{{ __('No matching results') }}
|
||||
</div>
|
||||
</div>
|
||||
</gl-new-dropdown>
|
||||
</gl-dropdown>
|
||||
</div>
|
||||
|
||||
<div class="mb-2 pr-2 d-flex d-sm-block">
|
||||
|
|
|
@ -6,7 +6,7 @@ import {
|
|||
GlIcon,
|
||||
GlLink,
|
||||
GlLoadingIcon,
|
||||
GlNewDropdown as GlDropdown,
|
||||
GlDropdown,
|
||||
GlNewDropdownItem as GlDropdownItem,
|
||||
GlNewDropdownDivider as GlDropdownDivider,
|
||||
GlModal,
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
import { mapState, mapGetters } from 'vuex';
|
||||
import {
|
||||
GlIcon,
|
||||
GlNewDropdown,
|
||||
GlDropdown,
|
||||
GlNewDropdownItem,
|
||||
GlNewDropdownHeader,
|
||||
GlNewDropdownDivider,
|
||||
|
@ -17,7 +17,7 @@ const events = {
|
|||
export default {
|
||||
components: {
|
||||
GlIcon,
|
||||
GlNewDropdown,
|
||||
GlDropdown,
|
||||
GlNewDropdownItem,
|
||||
GlNewDropdownHeader,
|
||||
GlNewDropdownDivider,
|
||||
|
@ -73,7 +73,7 @@ export default {
|
|||
};
|
||||
</script>
|
||||
<template>
|
||||
<gl-new-dropdown
|
||||
<gl-dropdown
|
||||
toggle-class="dropdown-menu-toggle"
|
||||
menu-class="monitor-dashboard-dropdown-menu"
|
||||
:text="selectedDashboardText"
|
||||
|
@ -127,5 +127,5 @@ export default {
|
|||
{{ __('No matching results') }}
|
||||
</div>
|
||||
</div>
|
||||
</gl-new-dropdown>
|
||||
</gl-dropdown>
|
||||
</template>
|
||||
|
|
|
@ -4,7 +4,7 @@ import { mapActions } from 'vuex';
|
|||
import {
|
||||
GlButtonGroup,
|
||||
GlButton,
|
||||
GlNewDropdown,
|
||||
GlDropdown,
|
||||
GlNewDropdownItem,
|
||||
GlNewDropdownDivider,
|
||||
GlTooltipDirective,
|
||||
|
@ -48,7 +48,7 @@ export default {
|
|||
components: {
|
||||
GlButtonGroup,
|
||||
GlButton,
|
||||
GlNewDropdown,
|
||||
GlDropdown,
|
||||
GlNewDropdownItem,
|
||||
GlNewDropdownDivider,
|
||||
},
|
||||
|
@ -152,7 +152,7 @@ export default {
|
|||
icon="retry"
|
||||
@click="refresh"
|
||||
/>
|
||||
<gl-new-dropdown
|
||||
<gl-dropdown
|
||||
v-if="!disableMetricDashboardRefreshRate"
|
||||
v-gl-tooltip
|
||||
:title="s__('Metrics|Set refresh rate')"
|
||||
|
@ -173,6 +173,6 @@ export default {
|
|||
@click="setRefreshInterval(option)"
|
||||
>{{ option.label }}</gl-new-dropdown-item
|
||||
>
|
||||
</gl-new-dropdown>
|
||||
</gl-dropdown>
|
||||
</gl-button-group>
|
||||
</template>
|
||||
|
|
|
@ -1,11 +1,12 @@
|
|||
<script>
|
||||
import { GlButton, GlModal, GlModalDirective } from '@gitlab/ui';
|
||||
import { escape } from 'lodash';
|
||||
import { GlModal, GlModalDirective } from '@gitlab/ui';
|
||||
import { s__, sprintf } from '~/locale';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
GlModal,
|
||||
GlButton,
|
||||
},
|
||||
directives: {
|
||||
'gl-modal': GlModalDirective,
|
||||
|
@ -55,14 +56,14 @@ export default {
|
|||
|
||||
<template>
|
||||
<div class="d-inline-block">
|
||||
<button
|
||||
<gl-button
|
||||
v-gl-modal="modalId"
|
||||
type="button"
|
||||
class="btn btn-danger"
|
||||
category="primary"
|
||||
variant="danger"
|
||||
data-qa-selector="delete_button"
|
||||
>
|
||||
{{ __('Delete') }}
|
||||
</button>
|
||||
</gl-button>
|
||||
<gl-modal
|
||||
:title="title"
|
||||
:action-primary="{
|
||||
|
|
|
@ -9,7 +9,7 @@ import {
|
|||
GlFormInput,
|
||||
GlFormSelect,
|
||||
GlLink,
|
||||
GlNewDropdown,
|
||||
GlDropdown,
|
||||
GlNewDropdownItem,
|
||||
GlSearchBoxByType,
|
||||
GlSprintf,
|
||||
|
@ -37,7 +37,7 @@ export default {
|
|||
GlFormInput,
|
||||
GlFormSelect,
|
||||
GlLink,
|
||||
GlNewDropdown,
|
||||
GlDropdown,
|
||||
GlNewDropdownItem,
|
||||
GlSearchBoxByType,
|
||||
GlSprintf,
|
||||
|
@ -173,7 +173,7 @@ export default {
|
|||
>{{ error }}</gl-alert
|
||||
>
|
||||
<gl-form-group :label="s__('Pipeline|Run for')">
|
||||
<gl-new-dropdown :text="refValue" block>
|
||||
<gl-dropdown :text="refValue" block>
|
||||
<gl-search-box-by-type
|
||||
v-model.trim="searchTerm"
|
||||
:placeholder="__('Search branches and tags')"
|
||||
|
@ -189,7 +189,7 @@ export default {
|
|||
>
|
||||
{{ ref }}
|
||||
</gl-new-dropdown-item>
|
||||
</gl-new-dropdown>
|
||||
</gl-dropdown>
|
||||
|
||||
<template #description>
|
||||
<div>
|
||||
|
|
|
@ -130,14 +130,9 @@ export default {
|
|||
{{ downstreamTitle }}
|
||||
</span>
|
||||
<div class="gl-text-truncate">
|
||||
<gl-link
|
||||
v-if="childPipeline"
|
||||
class="gl-text-blue-500!"
|
||||
:href="pipeline.path"
|
||||
data-testid="childPipelineLink"
|
||||
<gl-link class="gl-text-blue-500!" :href="pipeline.path" data-testid="pipelineLink"
|
||||
>#{{ pipeline.id }}</gl-link
|
||||
>
|
||||
<span v-else>#{{ pipeline.id }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { addIconStatus, formattedTime, sortTestCases } from './utils';
|
||||
import { addIconStatus, formattedTime } from './utils';
|
||||
|
||||
export const getTestSuites = state => {
|
||||
const { test_suites: testSuites = [] } = state.testReports;
|
||||
|
@ -14,5 +14,5 @@ export const getSelectedSuite = state =>
|
|||
|
||||
export const getSuiteTests = state => {
|
||||
const { test_cases: testCases = [] } = getSelectedSuite(state);
|
||||
return testCases.sort(sortTestCases).map(addIconStatus);
|
||||
return testCases.map(addIconStatus);
|
||||
};
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
import { TestStatus } from '~/pipelines/constants';
|
||||
import { __, sprintf } from '../../../locale';
|
||||
|
||||
export function iconForTestStatus(status) {
|
||||
|
@ -25,18 +24,3 @@ export const addIconStatus = testCase => ({
|
|||
icon: iconForTestStatus(testCase.status),
|
||||
formattedTime: formattedTime(testCase.execution_time),
|
||||
});
|
||||
|
||||
export const sortTestCases = (a, b) => {
|
||||
if (a.status === b.status) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
switch (b.status) {
|
||||
case TestStatus.SUCCESS:
|
||||
return -1;
|
||||
case TestStatus.FAILED:
|
||||
return 1;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
};
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
import { debounce } from 'lodash';
|
||||
import { mapState, mapActions } from 'vuex';
|
||||
import {
|
||||
GlNewDropdown,
|
||||
GlDropdown,
|
||||
GlNewDropdownHeader,
|
||||
GlNewDropdownItem,
|
||||
GlSearchBoxByType,
|
||||
|
@ -18,7 +18,7 @@ const tooltipMessage = __('Searching by both author and message is currently not
|
|||
export default {
|
||||
name: 'AuthorSelect',
|
||||
components: {
|
||||
GlNewDropdown,
|
||||
GlDropdown,
|
||||
GlNewDropdownHeader,
|
||||
GlNewDropdownItem,
|
||||
GlSearchBoxByType,
|
||||
|
@ -107,7 +107,7 @@ export default {
|
|||
|
||||
<template>
|
||||
<div ref="dropdownContainer" v-gl-tooltip :title="tooltipTitle" :disabled="!hasSearchParam">
|
||||
<gl-new-dropdown
|
||||
<gl-dropdown
|
||||
:text="dropdownText"
|
||||
:disabled="hasSearchParam"
|
||||
toggle-class="gl-py-3 gl-border-0"
|
||||
|
@ -137,6 +137,6 @@ export default {
|
|||
>
|
||||
{{ author.name }}
|
||||
</gl-new-dropdown-item>
|
||||
</gl-new-dropdown>
|
||||
</gl-dropdown>
|
||||
</div>
|
||||
</template>
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
<script>
|
||||
import { mapActions, mapGetters, mapState } from 'vuex';
|
||||
import {
|
||||
GlNewDropdown,
|
||||
GlDropdown,
|
||||
GlNewDropdownDivider,
|
||||
GlNewDropdownHeader,
|
||||
GlSearchBoxByType,
|
||||
|
@ -18,7 +18,7 @@ export default {
|
|||
name: 'RefSelector',
|
||||
store: createStore(),
|
||||
components: {
|
||||
GlNewDropdown,
|
||||
GlDropdown,
|
||||
GlNewDropdownDivider,
|
||||
GlNewDropdownHeader,
|
||||
GlSearchBoxByType,
|
||||
|
@ -120,7 +120,7 @@ export default {
|
|||
</script>
|
||||
|
||||
<template>
|
||||
<gl-new-dropdown v-bind="$attrs" class="ref-selector" @shown="focusSearchBox">
|
||||
<gl-dropdown v-bind="$attrs" class="ref-selector" @shown="focusSearchBox">
|
||||
<template slot="button-content">
|
||||
<span class="gl-flex-grow-1 gl-ml-2 gl-text-gray-400" data-testid="button-content">
|
||||
<span v-if="selectedRef" class="gl-font-monospace">{{ selectedRef }}</span>
|
||||
|
@ -208,5 +208,5 @@ export default {
|
|||
</template>
|
||||
</div>
|
||||
</div>
|
||||
</gl-new-dropdown>
|
||||
</gl-dropdown>
|
||||
</template>
|
||||
|
|
|
@ -59,7 +59,7 @@ export default {
|
|||
};
|
||||
},
|
||||
assigneeUrl() {
|
||||
return this.user.web_url;
|
||||
return this.user.web_url || this.user.webUrl;
|
||||
},
|
||||
},
|
||||
};
|
||||
|
|
|
@ -0,0 +1,37 @@
|
|||
<script>
|
||||
import { n__ } from '~/locale';
|
||||
import UncollapsedAssigneeList from '~/sidebar/components/assignees/uncollapsed_assignee_list.vue';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
UncollapsedAssigneeList,
|
||||
},
|
||||
inject: ['rootPath'],
|
||||
props: {
|
||||
users: {
|
||||
type: Array,
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
assigneesText() {
|
||||
return n__('Assignee', '%d Assignees', this.users.length);
|
||||
},
|
||||
emptyUsers() {
|
||||
return this.users.length === 0;
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="gl-display-flex gl-flex-direction-column">
|
||||
<label data-testid="assigneeLabel">{{ assigneesText }}</label>
|
||||
<div v-if="emptyUsers" data-testid="none">
|
||||
<span>
|
||||
{{ __('None') }}
|
||||
</span>
|
||||
</div>
|
||||
<uncollapsed-assignee-list v-else :users="users" :root-path="rootPath" />
|
||||
</div>
|
||||
</template>
|
|
@ -73,9 +73,9 @@ export default {
|
|||
:root-path="rootPath"
|
||||
:issuable-type="issuableType"
|
||||
>
|
||||
<div class="ml-2">
|
||||
<span class="author"> {{ user.name }} </span>
|
||||
<span class="username"> {{ username }} </span>
|
||||
<div class="ml-2 gl-line-height-normal">
|
||||
<div>{{ user.name }}</div>
|
||||
<div>{{ username }}</div>
|
||||
</div>
|
||||
</assignee-avatar-link>
|
||||
<div v-else>
|
||||
|
|
|
@ -55,6 +55,7 @@ function UsersSelect(currentUser, els, options = {}) {
|
|||
const defaultLabel = $dropdown.data('defaultLabel');
|
||||
const issueURL = $dropdown.data('issueUpdate');
|
||||
const $selectbox = $dropdown.closest('.selectbox');
|
||||
const $assignToMeLink = $selectbox.next('.assign-to-me-link');
|
||||
let $block = $selectbox.closest('.block');
|
||||
const abilityName = $dropdown.data('abilityName');
|
||||
let $value = $block.find('.value');
|
||||
|
@ -161,7 +162,7 @@ function UsersSelect(currentUser, els, options = {}) {
|
|||
});
|
||||
};
|
||||
|
||||
$('.assign-to-me-link').on('click', e => {
|
||||
$assignToMeLink.on('click', e => {
|
||||
e.preventDefault();
|
||||
$(e.currentTarget).hide();
|
||||
|
||||
|
@ -451,9 +452,9 @@ function UsersSelect(currentUser, els, options = {}) {
|
|||
}
|
||||
|
||||
if (getSelected().find(u => u === gon.current_user_id)) {
|
||||
$('.assign-to-me-link').hide();
|
||||
$assignToMeLink.hide();
|
||||
} else {
|
||||
$('.assign-to-me-link').show();
|
||||
$assignToMeLink.show();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -4,7 +4,7 @@ import Mousetrap from 'mousetrap';
|
|||
import { escape } from 'lodash';
|
||||
import {
|
||||
GlButton,
|
||||
GlNewDropdown as GlDropdown,
|
||||
GlDropdown,
|
||||
GlNewDropdownHeader as GlDropdownHeader,
|
||||
GlNewDropdownItem as GlDropdownItem,
|
||||
GlTooltipDirective,
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
<script>
|
||||
import {
|
||||
GlNewDropdown,
|
||||
GlDropdown,
|
||||
GlNewDropdownHeader,
|
||||
GlFormInputGroup,
|
||||
GlButton,
|
||||
|
@ -11,7 +11,7 @@ import { getHTTPProtocol } from '~/lib/utils/url_utility';
|
|||
|
||||
export default {
|
||||
components: {
|
||||
GlNewDropdown,
|
||||
GlDropdown,
|
||||
GlNewDropdownHeader,
|
||||
GlFormInputGroup,
|
||||
GlButton,
|
||||
|
@ -45,7 +45,7 @@ export default {
|
|||
};
|
||||
</script>
|
||||
<template>
|
||||
<gl-new-dropdown right :text="$options.labels.defaultLabel" category="primary" variant="info">
|
||||
<gl-dropdown right :text="$options.labels.defaultLabel" category="primary" variant="info">
|
||||
<div class="pb-2 mx-1">
|
||||
<template v-if="sshLink">
|
||||
<gl-new-dropdown-header>{{ $options.labels.ssh }}</gl-new-dropdown-header>
|
||||
|
@ -85,5 +85,5 @@ export default {
|
|||
</div>
|
||||
</template>
|
||||
</div>
|
||||
</gl-new-dropdown>
|
||||
</gl-dropdown>
|
||||
</template>
|
||||
|
|
|
@ -14,6 +14,11 @@ export default {
|
|||
type: Object,
|
||||
required: true,
|
||||
},
|
||||
fileUrl: {
|
||||
type: String,
|
||||
required: false,
|
||||
default: '',
|
||||
},
|
||||
level: {
|
||||
type: Number,
|
||||
required: true,
|
||||
|
@ -48,6 +53,9 @@ export default {
|
|||
// don't output a title if we don't have the expanded path
|
||||
return this.file?.tree?.length ? this.file.tree[0].parentPath : false;
|
||||
},
|
||||
fileRouterUrl() {
|
||||
return this.fileUrl || `/project${this.file.url}`;
|
||||
},
|
||||
},
|
||||
watch: {
|
||||
'file.active': function fileActiveWatch(active) {
|
||||
|
@ -74,7 +82,7 @@ export default {
|
|||
this.toggleTreeOpen(this.file.path);
|
||||
}
|
||||
|
||||
if (this.$router) this.$router.push(`/project${this.file.url}`);
|
||||
if (this.$router && !this.hasUrlAtCurrentRoute()) this.$router.push(this.fileRouterUrl);
|
||||
|
||||
if (this.isBlob) this.clickedFile(this.file.path);
|
||||
},
|
||||
|
@ -104,7 +112,7 @@ export default {
|
|||
hasUrlAtCurrentRoute() {
|
||||
if (!this.$router || !this.$router.currentRoute) return true;
|
||||
|
||||
return this.$router.currentRoute.path === `/project${escapeFileUrl(this.file.url)}`;
|
||||
return this.$router.currentRoute.path === escapeFileUrl(this.fileRouterUrl);
|
||||
},
|
||||
},
|
||||
};
|
||||
|
|
|
@ -3,7 +3,7 @@ import {
|
|||
GlFilteredSearch,
|
||||
GlButtonGroup,
|
||||
GlButton,
|
||||
GlNewDropdown as GlDropdown,
|
||||
GlDropdown,
|
||||
GlNewDropdownItem as GlDropdownItem,
|
||||
GlTooltipDirective,
|
||||
} from '@gitlab/ui';
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
export const DEFAULT_RX = 0.4;
|
||||
export const DEFAULT_BAR_WIDTH = 6;
|
||||
export const DEFAULT_LABEL_WIDTH = 4;
|
||||
export const DEFAULT_LABEL_HEIGHT = 5;
|
||||
export const DEFAULT_BAR_WIDTH = 4;
|
||||
export const DEFAULT_LABEL_WIDTH = 3;
|
||||
export const DEFAULT_LABEL_HEIGHT = 3;
|
||||
export const BAR_HEIGHTS = [5, 7, 9, 14, 21, 35, 50, 80];
|
||||
export const GRID_YS = [30, 60, 90];
|
||||
|
|
|
@ -61,35 +61,37 @@ export default {
|
|||
};
|
||||
</script>
|
||||
<template>
|
||||
<gl-skeleton-loader :unique-key="uniqueKey">
|
||||
<rect
|
||||
v-for="(y, index) in $options.GRID_YS"
|
||||
:key="`grid-${index}`"
|
||||
data-testid="skeleton-chart-grid"
|
||||
x="0"
|
||||
:y="`${y}%`"
|
||||
width="100%"
|
||||
height="1px"
|
||||
/>
|
||||
<rect
|
||||
v-for="(height, index) in $options.BAR_HEIGHTS"
|
||||
:key="`bar-${index}`"
|
||||
data-testid="skeleton-chart-bar"
|
||||
:x="`${getBarXPosition(index)}%`"
|
||||
:y="`${90 - height}%`"
|
||||
:width="`${barWidth}%`"
|
||||
:height="`${height}%`"
|
||||
:rx="`${rx}%`"
|
||||
/>
|
||||
<rect
|
||||
v-for="(height, index) in $options.BAR_HEIGHTS"
|
||||
:key="`label-${index}`"
|
||||
data-testid="skeleton-chart-label"
|
||||
:x="`${labelCentering + getBarXPosition(index)}%`"
|
||||
:y="`${100 - labelHeight}%`"
|
||||
:width="`${labelWidth}%`"
|
||||
:height="`${labelHeight}%`"
|
||||
:rx="`${rx}%`"
|
||||
/>
|
||||
</gl-skeleton-loader>
|
||||
<div class="gl-px-8">
|
||||
<gl-skeleton-loader :unique-key="uniqueKey" class="gl-p-8">
|
||||
<rect
|
||||
v-for="(y, index) in $options.GRID_YS"
|
||||
:key="`grid-${index}`"
|
||||
data-testid="skeleton-chart-grid"
|
||||
x="0"
|
||||
:y="`${y}%`"
|
||||
width="100%"
|
||||
height="1px"
|
||||
/>
|
||||
<rect
|
||||
v-for="(height, index) in $options.BAR_HEIGHTS"
|
||||
:key="`bar-${index}`"
|
||||
data-testid="skeleton-chart-bar"
|
||||
:x="`${getBarXPosition(index)}%`"
|
||||
:y="`${90 - height}%`"
|
||||
:width="`${barWidth}%`"
|
||||
:height="`${height}%`"
|
||||
:rx="`${rx}%`"
|
||||
/>
|
||||
<rect
|
||||
v-for="(height, index) in $options.BAR_HEIGHTS"
|
||||
:key="`label-${index}`"
|
||||
data-testid="skeleton-chart-label"
|
||||
:x="`${labelCentering + getBarXPosition(index)}%`"
|
||||
:y="`${100 - labelHeight}%`"
|
||||
:width="`${labelWidth}%`"
|
||||
:height="`${labelHeight}%`"
|
||||
:rx="`${rx}%`"
|
||||
/>
|
||||
</gl-skeleton-loader>
|
||||
</div>
|
||||
</template>
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
<script>
|
||||
import { GlNewDropdown, GlDeprecatedDropdownItem, GlSearchBoxByType, GlIcon } from '@gitlab/ui';
|
||||
import { GlDropdown, GlDeprecatedDropdownItem, GlSearchBoxByType, GlIcon } from '@gitlab/ui';
|
||||
import { __ } from '~/locale';
|
||||
import autofocusonshow from '~/vue_shared/directives/autofocusonshow';
|
||||
|
||||
export default {
|
||||
name: 'TimezoneDropdown',
|
||||
components: {
|
||||
GlNewDropdown,
|
||||
GlDropdown,
|
||||
GlDeprecatedDropdownItem,
|
||||
GlSearchBoxByType,
|
||||
GlIcon,
|
||||
|
@ -74,7 +74,7 @@ export default {
|
|||
};
|
||||
</script>
|
||||
<template>
|
||||
<gl-new-dropdown :text="value" block lazy menu-class="gl-w-full!">
|
||||
<gl-dropdown :text="value" block lazy menu-class="gl-w-full!">
|
||||
<template #button-content>
|
||||
<span class="gl-flex-grow-1" :class="{ 'gl-text-gray-300': !value }">
|
||||
{{ selectedTimezoneLabel }}
|
||||
|
@ -98,5 +98,5 @@ export default {
|
|||
<gl-deprecated-dropdown-item v-if="!filteredResults.length" data-testid="noMatchingResults">
|
||||
{{ $options.tranlations.noResultsText }}
|
||||
</gl-deprecated-dropdown-item>
|
||||
</gl-new-dropdown>
|
||||
</gl-dropdown>
|
||||
</template>
|
||||
|
|
|
@ -116,6 +116,7 @@
|
|||
|
||||
.board-title {
|
||||
flex-direction: column;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.board-title-caret {
|
||||
|
|
|
@ -361,13 +361,6 @@
|
|||
margin: 0;
|
||||
}
|
||||
|
||||
.username {
|
||||
display: block;
|
||||
margin-top: 4px;
|
||||
font-size: 13px;
|
||||
font-weight: $gl-font-weight-normal;
|
||||
}
|
||||
|
||||
.hide-expanded {
|
||||
display: none;
|
||||
}
|
||||
|
|
|
@ -55,6 +55,29 @@ module FormHelper
|
|||
dropdown_data
|
||||
end
|
||||
|
||||
def reviewers_dropdown_options(issuable_type)
|
||||
{
|
||||
toggle_class: 'js-reviewer-search js-multiselect js-save-user-data',
|
||||
title: 'Request review from',
|
||||
filter: true,
|
||||
dropdown_class: 'dropdown-menu-user dropdown-menu-selectable dropdown-menu-reviewer',
|
||||
placeholder: _('Search users'),
|
||||
data: {
|
||||
first_user: current_user&.username,
|
||||
null_user: true,
|
||||
current_user: true,
|
||||
project_id: (@target_project || @project)&.id,
|
||||
field_name: "#{issuable_type}[reviewer_ids][]",
|
||||
default_label: 'Unassigned',
|
||||
'dropdown-header': 'Reviewer(s)',
|
||||
multi_select: true,
|
||||
'input-meta': 'name',
|
||||
'always-show-selectbox': true,
|
||||
current_user_info: UserSerializer.new.represent(current_user)
|
||||
}
|
||||
}
|
||||
end
|
||||
|
||||
# Overwritten
|
||||
def issue_supports_multiple_assignees?
|
||||
false
|
||||
|
|
|
@ -181,7 +181,7 @@ module NotesHelper
|
|||
reopenPath: reopen_issuable_path(issuable),
|
||||
notesPath: notes_url,
|
||||
prerenderedNotesCount: issuable.capped_notes_count(MAX_PRERENDERED_NOTES),
|
||||
lastFetchedAt: Time.now.to_i
|
||||
lastFetchedAt: Time.now.to_i * ::Gitlab::UpdatedNotesPaginator::MICROSECOND
|
||||
}
|
||||
|
||||
if issuable.is_a?(MergeRequest)
|
||||
|
|
|
@ -6,15 +6,15 @@ module NotificationsHelper
|
|||
def notification_icon_class(level)
|
||||
case level.to_sym
|
||||
when :disabled, :owner_disabled
|
||||
'microphone-slash'
|
||||
'notifications-off'
|
||||
when :participating
|
||||
'volume-up'
|
||||
'notifications'
|
||||
when :watch
|
||||
'eye'
|
||||
when :mention
|
||||
'at'
|
||||
when :global
|
||||
'globe'
|
||||
'earth'
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -28,8 +28,8 @@ module NotificationsHelper
|
|||
end
|
||||
end
|
||||
|
||||
def notification_icon(level, text = nil)
|
||||
icon("#{notification_icon_class(level)} fw", text: text)
|
||||
def notification_icon(level)
|
||||
sprite_icon("#{notification_icon_class(level)}")
|
||||
end
|
||||
|
||||
def notification_title(level)
|
||||
|
|
|
@ -32,8 +32,6 @@ module AlertManagement
|
|||
:acknowledged
|
||||
].freeze
|
||||
|
||||
DETAILS_IGNORED_PARAMS = %w(start_time).freeze
|
||||
|
||||
belongs_to :project
|
||||
belongs_to :issue, optional: true
|
||||
belongs_to :prometheus_alert, optional: true
|
||||
|
@ -119,7 +117,7 @@ module AlertManagement
|
|||
end
|
||||
|
||||
delegate :iid, to: :issue, prefix: true, allow_nil: true
|
||||
delegate :metrics_dashboard_url, :details_url, to: :present
|
||||
delegate :metrics_dashboard_url, :details_url, :details, to: :present
|
||||
|
||||
scope :for_iid, -> (iid) { where(iid: iid) }
|
||||
scope :for_status, -> (status) { where(status: status) }
|
||||
|
@ -172,12 +170,6 @@ module AlertManagement
|
|||
with_prometheus_alert.where(id: ids)
|
||||
end
|
||||
|
||||
def details
|
||||
details_payload = payload.except(*attributes.keys, *DETAILS_IGNORED_PARAMS)
|
||||
|
||||
Gitlab::Utils::InlineHash.merge_keys(details_payload)
|
||||
end
|
||||
|
||||
def prometheus?
|
||||
monitoring_tool == Gitlab::AlertManagement::AlertParams::MONITORING_TOOLS[:prometheus]
|
||||
end
|
||||
|
|
|
@ -146,6 +146,7 @@ class Project < ApplicationRecord
|
|||
has_one :discord_service
|
||||
has_one :drone_ci_service
|
||||
has_one :emails_on_push_service
|
||||
has_one :ewm_service
|
||||
has_one :pipelines_email_service
|
||||
has_one :irker_service
|
||||
has_one :pivotaltracker_service
|
||||
|
|
|
@ -0,0 +1,29 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class EwmService < IssueTrackerService
|
||||
validates :project_url, :issues_url, :new_issue_url, presence: true, public_url: true, if: :activated?
|
||||
|
||||
def self.reference_pattern(only_long: true)
|
||||
@reference_pattern ||= %r{(?<issue>\b(bug|task|work item|workitem|rtcwi|defect)\b\s+\d+)}i
|
||||
end
|
||||
|
||||
def title
|
||||
'EWM'
|
||||
end
|
||||
|
||||
def description
|
||||
s_('IssueTracker|EWM work items tracker')
|
||||
end
|
||||
|
||||
def self.to_param
|
||||
'ewm'
|
||||
end
|
||||
|
||||
def can_test?
|
||||
false
|
||||
end
|
||||
|
||||
def issue_url(iid)
|
||||
issues_url.gsub(':id', iid.to_s.split(' ')[-1])
|
||||
end
|
||||
end
|
|
@ -13,7 +13,7 @@ class Service < ApplicationRecord
|
|||
|
||||
SERVICE_NAMES = %w[
|
||||
alerts asana assembla bamboo bugzilla buildkite campfire confluence custom_issue_tracker discord
|
||||
drone_ci emails_on_push external_wiki flowdock hangouts_chat hipchat irker jira
|
||||
drone_ci emails_on_push ewm external_wiki flowdock hangouts_chat hipchat irker jira
|
||||
mattermost mattermost_slash_commands microsoft_teams packagist pipelines_email
|
||||
pivotaltracker prometheus pushover redmine slack slack_slash_commands teamcity unify_circuit webex_teams youtrack
|
||||
].freeze
|
||||
|
|
|
@ -42,6 +42,10 @@ module AlertManagement
|
|||
details_project_alert_management_url(project, alert.iid)
|
||||
end
|
||||
|
||||
def details
|
||||
Gitlab::Utils::InlineHash.merge_keys(payload)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
attr_reader :alert, :project
|
||||
|
@ -81,7 +85,7 @@ module AlertManagement
|
|||
end
|
||||
|
||||
def details_list
|
||||
alert.details
|
||||
details
|
||||
.map { |label, value| list_item(label, value) }
|
||||
.join(MARKDOWN_LINE_BREAK)
|
||||
end
|
||||
|
|
|
@ -12,10 +12,6 @@ module AlertManagement
|
|||
alerting_alert.alert_markdown
|
||||
end
|
||||
|
||||
def details_list
|
||||
alerting_alert.annotation_list
|
||||
end
|
||||
|
||||
def metric_embed_for_alert
|
||||
alerting_alert.metric_embed_for_alert
|
||||
end
|
||||
|
|
|
@ -3,7 +3,6 @@
|
|||
module Projects
|
||||
module Prometheus
|
||||
class AlertPresenter < Gitlab::View::Presenter::Delegated
|
||||
RESERVED_ANNOTATIONS = %w(gitlab_incident_markdown gitlab_y_label title).freeze
|
||||
GENERIC_ALERT_SUMMARY_ANNOTATIONS = %w(monitoring_tool service hosts).freeze
|
||||
MARKDOWN_LINE_BREAK = " \n".freeze
|
||||
INCIDENT_LABEL_NAME = ::IncidentManagement::CreateIncidentLabelService::LABEL_PROPERTIES[:title].freeze
|
||||
|
@ -56,11 +55,10 @@ module Projects
|
|||
MARKDOWN
|
||||
end
|
||||
|
||||
def annotation_list
|
||||
strong_memoize(:annotation_list) do
|
||||
annotations
|
||||
.reject { |annotation| annotation.label.in?(RESERVED_ANNOTATIONS | GENERIC_ALERT_SUMMARY_ANNOTATIONS) }
|
||||
.map { |annotation| list_item(annotation.label, annotation.value) }
|
||||
def details_list
|
||||
strong_memoize(:details_list) do
|
||||
details
|
||||
.map { |label, value| list_item(label, value) }
|
||||
.join(MARKDOWN_LINE_BREAK)
|
||||
end
|
||||
end
|
||||
|
@ -109,13 +107,17 @@ module Projects
|
|||
metadata.join(MARKDOWN_LINE_BREAK)
|
||||
end
|
||||
|
||||
def details
|
||||
Gitlab::Utils::InlineHash.merge_keys(payload)
|
||||
end
|
||||
|
||||
def alert_details
|
||||
if annotation_list.present?
|
||||
if details.present?
|
||||
<<~MARKDOWN.chomp
|
||||
|
||||
#### Alert Details
|
||||
|
||||
#{annotation_list}
|
||||
#{details_list}
|
||||
MARKDOWN
|
||||
end
|
||||
end
|
||||
|
|
|
@ -44,6 +44,8 @@ module SystemNotes
|
|||
def change_assignee(assignee)
|
||||
body = assignee.nil? ? 'removed assignee' : "assigned to #{assignee.to_reference}"
|
||||
|
||||
issue_activity_counter.track_issue_assignee_changed_action(author: author) if noteable.is_a?(Issue)
|
||||
|
||||
create_note(NoteSummary.new(noteable, project, author, body, action: 'assignee'))
|
||||
end
|
||||
|
||||
|
@ -74,6 +76,8 @@ module SystemNotes
|
|||
|
||||
body = text_parts.join(' and ')
|
||||
|
||||
issue_activity_counter.track_issue_assignee_changed_action(author: author) if noteable.is_a?(Issue)
|
||||
|
||||
create_note(NoteSummary.new(noteable, project, author, body, action: 'assignee'))
|
||||
end
|
||||
|
||||
|
@ -96,6 +100,8 @@ module SystemNotes
|
|||
|
||||
body = "changed title from **#{marked_old_title}** to **#{marked_new_title}**"
|
||||
|
||||
issue_activity_counter.track_issue_title_changed_action(author: author) if noteable.is_a?(Issue)
|
||||
|
||||
create_note(NoteSummary.new(noteable, project, author, body, action: 'title'))
|
||||
end
|
||||
|
||||
|
@ -113,6 +119,8 @@ module SystemNotes
|
|||
def change_description
|
||||
body = 'changed the description'
|
||||
|
||||
issue_activity_counter.track_issue_description_changed_action(author: author) if noteable.is_a?(Issue)
|
||||
|
||||
create_note(NoteSummary.new(noteable, project, author, body, action: 'description'))
|
||||
end
|
||||
|
||||
|
@ -209,9 +217,13 @@ module SystemNotes
|
|||
if noteable.confidential
|
||||
body = 'made the issue confidential'
|
||||
action = 'confidential'
|
||||
|
||||
issue_activity_counter.track_issue_made_confidential_action(author: author) if noteable.is_a?(Issue)
|
||||
else
|
||||
body = 'made the issue visible to everyone'
|
||||
action = 'visible'
|
||||
|
||||
issue_activity_counter.track_issue_made_visible_action(author: author) if noteable.is_a?(Issue)
|
||||
end
|
||||
|
||||
create_note(NoteSummary.new(noteable, project, author, body, action: action))
|
||||
|
@ -353,6 +365,10 @@ module SystemNotes
|
|||
noteable.respond_to?(:resource_state_events) &&
|
||||
::Feature.enabled?(:track_resource_state_change_events, noteable.project, default_enabled: true)
|
||||
end
|
||||
|
||||
def issue_activity_counter
|
||||
Gitlab::UsageDataCounters::IssueActivityUniqueCounter
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
.gl-responsive-table-row.notification-list-item
|
||||
.table-section.section-40
|
||||
%span.notification.fa.fa-holder.gl-mr-2
|
||||
%span.notification.gl-mr-2
|
||||
= notification_icon(notification_icon_level(setting, emails_disabled))
|
||||
|
||||
%span.str-truncated
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
- emails_disabled = project.emails_disabled?
|
||||
|
||||
%li.notification-list-item
|
||||
%span.notification.fa.fa-holder.gl-mr-2
|
||||
%span.notification.gl-mr-2
|
||||
= notification_icon(notification_icon_level(setting, emails_disabled))
|
||||
|
||||
%span.str-truncated
|
||||
|
|
|
@ -1,9 +1,12 @@
|
|||
- if show_no_password_message?
|
||||
.no-password-message.alert.alert-warning
|
||||
- translation_params = { protocol: gitlab_config.protocol.upcase, set_password_link: link_to_set_password }
|
||||
- set_password_message = _("You won't be able to pull or push project code via %{protocol} until you %{set_password_link} on your account") % translation_params
|
||||
= set_password_message.html_safe
|
||||
.alert-link-group
|
||||
= link_to _("Don't show again"), profile_path(user: {hide_no_password: true}), method: :put
|
||||
|
|
||||
= link_to _('Remind later'), '#', class: 'hide-no-password-message'
|
||||
.no-password-message.gl-alert.gl-alert-warning
|
||||
= sprite_icon('warning', size: 16, css_class: 'gl-icon gl-alert-icon gl-alert-icon-no-title')
|
||||
%button.js-close.gl-alert-dismiss{ type: 'button', 'aria-label': _('Dismiss') }
|
||||
= sprite_icon('close', size: 16, css_class: 'gl-icon')
|
||||
.gl-alert-body
|
||||
- translation_params = { protocol: gitlab_config.protocol.upcase, set_password_link: link_to_set_password }
|
||||
- set_password_message = _("You won't be able to pull or push project code via %{protocol} until you %{set_password_link} on your account") % translation_params
|
||||
= set_password_message.html_safe
|
||||
.gl-alert-actions
|
||||
= link_to _('Remind later'), '#', class: 'hide-no-password-message btn gl-alert-action btn-info btn-md gl-button'
|
||||
= link_to _("Don't show again"), profile_path(user: {hide_no_password: true}), method: :put, role: 'button', class: 'btn gl-alert-action btn-md btn-default gl-button btn-default-secondary'
|
||||
|
|
|
@ -19,7 +19,7 @@
|
|||
#board-app.boards-app.position-relative{ "v-cloak" => "true", data: board_data, ":class" => "{ 'is-compact': detailIssueVisible }" }
|
||||
= render 'shared/issuable/search_bar', type: :boards, board: board
|
||||
|
||||
- if Feature.enabled?(:boards_with_swimlanes, current_board_parent) || Feature.enabled?(:graphql_board_lists, current_board_parent)
|
||||
- if Feature.enabled?(:boards_with_swimlanes, current_board_parent)
|
||||
%board-content{ "v-cloak" => "true",
|
||||
"ref" => "board_content",
|
||||
":lists" => "state.lists",
|
||||
|
|
|
@ -12,6 +12,10 @@
|
|||
.form-group.row.merge-request-assignee
|
||||
= render "shared/issuable/form/metadata_issuable_assignee", issuable: issuable, form: form, has_due_date: has_due_date
|
||||
|
||||
- if issuable.allows_reviewers?
|
||||
.form-group.row.merge-request-reviewer
|
||||
= render "shared/issuable/form/metadata_issuable_reviewer", issuable: issuable, form: form, has_due_date: has_due_date
|
||||
|
||||
= render_if_exists "shared/issuable/form/epic", issuable: issuable, form: form, project: project
|
||||
|
||||
- if issuable.supports_milestone?
|
||||
|
|
|
@ -0,0 +1,10 @@
|
|||
= form.label :reviewer_id, "Reviewer", class: "col-form-label #{has_due_date ? "col-md-2 col-lg-4" : "col-sm-2"}"
|
||||
.col-sm-10{ class: ("col-md-8" if has_due_date) }
|
||||
.issuable-form-select-holder.selectbox
|
||||
- issuable.reviewers.each do |reviewer|
|
||||
= hidden_field_tag "#{issuable.to_ability_name}[reviewer_ids][]", reviewer.id, id: nil, data: { meta: reviewer.name, avatar_url: reviewer.avatar_url, name: reviewer.name, username: reviewer.username }
|
||||
|
||||
- if issuable.reviewers.empty?
|
||||
= hidden_field_tag "#{issuable.to_ability_name}[reviewer_ids][]", 0, id: nil, data: { meta: '' }
|
||||
|
||||
= dropdown_tag(users_dropdown_label(issuable.reviewers), options: reviewers_dropdown_options(issuable.to_ability_name))
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Use the correct start time when polling for updated notes
|
||||
merge_request: 42124
|
||||
author:
|
||||
type: fixed
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Add admin setting of Elasticsearch client request timeout
|
||||
merge_request: 41470
|
||||
author:
|
||||
type: added
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Add Issue actions to UsageData
|
||||
merge_request: 40904
|
||||
author:
|
||||
type: other
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Replace bootstrap alerts in app/views/shared/_no_password.html.haml
|
||||
merge_request: 41397
|
||||
author: Gilang Gumilar
|
||||
type: changed
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Added EWM work item tracker integration
|
||||
merge_request: 36662
|
||||
author:
|
||||
type: added
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Make Pipeline ID's always a link for downstream/upstream pipelines
|
||||
merge_request: 42107
|
||||
author:
|
||||
type: added
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Remove frontend unit test report test case sorting
|
||||
merge_request: 40885
|
||||
author:
|
||||
type: changed
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Replace notification icons with Gitlab SVGs
|
||||
merge_request: 40709
|
||||
author:
|
||||
type: changed
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Remove duplicated container scanning findings
|
||||
merge_request: 42041
|
||||
author:
|
||||
type: other
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Present complete alert payload in detail and incident views
|
||||
merge_request: 42140
|
||||
author:
|
||||
type: changed
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Exclude tmp dirs from backups
|
||||
merge_request: 41706
|
||||
author:
|
||||
type: fixed
|
|
@ -0,0 +1,7 @@
|
|||
---
|
||||
name: track_issue_activity_actions
|
||||
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/40904
|
||||
rollout_issue_url:
|
||||
group: group::project_management
|
||||
type: development
|
||||
default_enabled: false
|
|
@ -0,0 +1,12 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
# See https://docs.gitlab.com/ee/development/migration_style_guide.html
|
||||
# for more information on how to write migrations for GitLab.
|
||||
|
||||
class AddElasticsearchClientTimeout < ActiveRecord::Migration[6.0]
|
||||
DOWNTIME = false
|
||||
|
||||
def change
|
||||
add_column :application_settings, :elasticsearch_client_request_timeout, :integer, null: false, default: 0
|
||||
end
|
||||
end
|
|
@ -0,0 +1,20 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class TmpIndexForFixingInconsistentVulnerabilityOccurrences < ActiveRecord::Migration[6.0]
|
||||
include Gitlab::Database::MigrationHelpers
|
||||
|
||||
DOWNTIME = false
|
||||
INDEX_NAME = 'tmp_index_for_fixing_inconsistent_vulnerability_occurrences'
|
||||
disable_ddl_transaction!
|
||||
|
||||
def up
|
||||
# report_type: 2 container scanning
|
||||
add_concurrent_index(:vulnerability_occurrences, :id,
|
||||
where: "LENGTH(location_fingerprint) = 40 AND report_type = 2",
|
||||
name: INDEX_NAME)
|
||||
end
|
||||
|
||||
def down
|
||||
remove_concurrent_index_by_name(:vulnerability_occurrences, INDEX_NAME)
|
||||
end
|
||||
end
|
|
@ -0,0 +1,31 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class RemoveDuplicatedCsFindings < ActiveRecord::Migration[6.0]
|
||||
include Gitlab::Database::MigrationHelpers
|
||||
|
||||
DOWNTIME = false
|
||||
|
||||
disable_ddl_transaction!
|
||||
|
||||
BATCH_SIZE = 1_000
|
||||
INTERVAL = 2.minutes
|
||||
|
||||
# 23_893 records will be updated
|
||||
# 23_893 records will be deleted
|
||||
def up
|
||||
return unless Gitlab.com?
|
||||
|
||||
migration = Gitlab::BackgroundMigration::RemoveDuplicateCsFindings
|
||||
migration_name = migration.to_s.demodulize
|
||||
relation = migration::Finding.container_scanning.where("LENGTH(location_fingerprint) = 40")
|
||||
queue_background_migration_jobs_by_range_at_intervals(relation,
|
||||
migration_name,
|
||||
INTERVAL,
|
||||
batch_size: BATCH_SIZE)
|
||||
end
|
||||
|
||||
def down
|
||||
# no-op
|
||||
# intentionally blank
|
||||
end
|
||||
end
|
|
@ -0,0 +1 @@
|
|||
f9aa112661a55c9eeed1a6aa05dd4c28d6f88971dc14bb606e677d4b4a4e5947
|
|
@ -0,0 +1 @@
|
|||
205580c1ba38fd03ce025dfd1d9be67756ade4fd28ba957bb71cd9e5e89ef190
|
|
@ -0,0 +1 @@
|
|||
ba431f19818b93da91c4ed2c3f25dc8e2f62c6d9ac07b15f6d01f21f085c1730
|
|
@ -9271,6 +9271,7 @@ CREATE TABLE public.application_settings (
|
|||
elasticsearch_indexed_file_size_limit_kb integer DEFAULT 1024 NOT NULL,
|
||||
enforce_namespace_storage_limit boolean DEFAULT false NOT NULL,
|
||||
container_registry_delete_tags_service_timeout integer DEFAULT 250 NOT NULL,
|
||||
elasticsearch_client_request_timeout integer DEFAULT 0 NOT NULL,
|
||||
CONSTRAINT check_51700b31b5 CHECK ((char_length(default_branch_name) <= 255)),
|
||||
CONSTRAINT check_9c6c447a13 CHECK ((char_length(maintenance_mode_message) <= 255)),
|
||||
CONSTRAINT check_d03919528d CHECK ((char_length(container_registry_vendor) <= 255)),
|
||||
|
@ -21400,6 +21401,8 @@ CREATE INDEX tmp_build_stage_position_index ON public.ci_builds USING btree (sta
|
|||
|
||||
CREATE INDEX tmp_index_for_email_unconfirmation_migration ON public.emails USING btree (id) WHERE (confirmed_at IS NOT NULL);
|
||||
|
||||
CREATE INDEX tmp_index_for_fixing_inconsistent_vulnerability_occurrences ON public.vulnerability_occurrences USING btree (id) WHERE ((length(location_fingerprint) = 40) AND (report_type = 2));
|
||||
|
||||
CREATE UNIQUE INDEX unique_merge_request_metrics_by_merge_request_id ON public.merge_request_metrics USING btree (merge_request_id);
|
||||
|
||||
CREATE UNIQUE INDEX users_security_dashboard_projects_unique_index ON public.users_security_dashboard_projects USING btree (project_id, user_id);
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue