Commit cb9c1bd8 authored by DJ Mountney's avatar DJ Mountney

Merge remote-tracking branch 'origin/master' into dev-master

parents 0db52ff7 8956d153
......@@ -40,6 +40,7 @@ eslint-report.html
/config/redis.queues.yml
/config/redis.shared_state.yml
/config/unicorn.rb
/config/puma.rb
/config/secrets.yml
/config/sidekiq.yml
/config/registry.key
......
......@@ -1192,6 +1192,5 @@ schedule:review_apps_cleanup:
- schedules@gitlab-org/gitlab-ee
kubernetes: active
except:
- master
- tags
- /(^docs[\/-].*|.*-docs$)/
......@@ -76,10 +76,14 @@ Naming/FileName:
- 'qa/qa/specs/**/*'
- 'qa/bin/*'
- 'config/**/*'
- 'ee/config/**/*'
- 'lib/generators/**/*'
- 'locale/unfound_translations.rb'
- 'ee/locale/unfound_translations.rb'
- 'ee/lib/generators/**/*'
- 'qa/qa/scenario/test/integration/ldap_no_tls.rb'
- 'qa/qa/scenario/test/integration/ldap_tls.rb'
IgnoreExecutableScripts: true
AllowedAcronyms:
- EE
......
8.4.0
\ No newline at end of file
8.4.1
......@@ -163,6 +163,11 @@ group :unicorn do
gem 'unicorn-worker-killer', '~> 0.4.4'
end
group :puma do
gem 'puma', '~> 3.12', require: false
gem 'puma_worker_killer', require: false
end
# State machine
gem 'state_machines-activerecord', '~> 0.5.1'
......
......@@ -576,7 +576,7 @@ GEM
orm_adapter (0.5.0)
os (1.0.0)
parallel (1.12.1)
parser (2.5.1.0)
parser (2.5.1.2)
ast (~> 2.4.0)
parslet (1.8.2)
peek (1.0.1)
......@@ -627,6 +627,10 @@ GEM
pry-rails (0.3.6)
pry (>= 0.10.4)
public_suffix (3.0.3)
puma (3.12.0)
puma_worker_killer (0.1.0)
get_process_mem (~> 0.2)
puma (>= 2.7, < 4)
pyu-ruby-sasl (0.0.3.3)
rack (1.6.10)
rack-accept (0.4.5)
......@@ -1113,6 +1117,8 @@ DEPENDENCIES
prometheus-client-mmap (~> 0.9.4)
pry-byebug (~> 3.4.1)
pry-rails (~> 0.3.4)
puma (~> 3.12)
puma_worker_killer
rack-attack (~> 4.4.1)
rack-cors (~> 1.0.0)
rack-oauth2 (~> 1.2.1)
......
......@@ -580,7 +580,7 @@ GEM
orm_adapter (0.5.0)
os (1.0.0)
parallel (1.12.1)
parser (2.5.1.0)
parser (2.5.1.2)
ast (~> 2.4.0)
parslet (1.8.2)
peek (1.0.1)
......@@ -631,6 +631,10 @@ GEM
pry-rails (0.3.6)
pry (>= 0.10.4)
public_suffix (3.0.3)
puma (3.12.0)
puma_worker_killer (0.1.0)
get_process_mem (~> 0.2)
puma (>= 2.7, < 4)
pyu-ruby-sasl (0.0.3.3)
rack (2.0.5)
rack-accept (0.4.5)
......@@ -1122,6 +1126,8 @@ DEPENDENCIES
prometheus-client-mmap (~> 0.9.4)
pry-byebug (~> 3.4.1)
pry-rails (~> 0.3.4)
puma (~> 3.12)
puma_worker_killer
rack-attack (~> 4.4.1)
rack-cors (~> 1.0.0)
rack-oauth2 (~> 1.2.1)
......
......@@ -53,6 +53,9 @@ export default Vue.extend({
const { issuesSize } = this.list;
return `${n__('%d issue', '%d issues', issuesSize)}`;
},
isNewIssueShown() {
return this.list.type === 'backlog' || (!this.disabled && this.list.type !== 'closed');
}
},
watch: {
filter: {
......
......@@ -46,7 +46,7 @@ export default {
selectable: true,
data: (term, callback) => {
this.loading = true;
return Api.groupProjects(this.groupId, term, {}, projects => {
return Api.groupProjects(this.groupId, term, {with_issues_enabled: true}, projects => {
this.loading = false;
callback(projects);
});
......
......@@ -41,6 +41,11 @@ export default {
required: true,
},
},
data() {
return {
assignedDiscussions: false,
};
},
computed: {
...mapState({
isLoading: state => state.diffs.isLoading,
......@@ -58,9 +63,9 @@ export default {
plainDiffPath: state => state.diffs.plainDiffPath,
emailPatchPath: state => state.diffs.emailPatchPath,
}),
...mapState('diffs', ['showTreeList']),
...mapState('diffs', ['showTreeList', 'isLoading']),
...mapGetters('diffs', ['isParallelView']),
...mapGetters(['isNotesFetched', 'discussionsStructuredByLineCode']),
...mapGetters(['isNotesFetched', 'getNoteableData']),
targetBranch() {
return {
branchName: this.targetBranchName,
......@@ -147,11 +152,12 @@ export default {
}
},
setDiscussions() {
if (this.isNotesFetched) {
if (this.isNotesFetched && !this.assignedDiscussions && !this.isLoading) {
requestIdleCallback(
() => {
this.assignDiscussionsToDiff(this.discussionsStructuredByLineCode);
},
() =>
this.assignDiscussionsToDiff().then(() => {
this.assignedDiscussions = true;
}),
{ timeout: 1000 },
);
}
......
......@@ -29,7 +29,7 @@ export default {
},
computed: {
...mapState('diffs', ['currentDiffFileId']),
...mapGetters(['isNotesFetched', 'discussionsStructuredByLineCode']),
...mapGetters(['isNotesFetched']),
isCollapsed() {
return this.file.collapsed || false;
},
......@@ -79,7 +79,7 @@ export default {
.then(() => {
requestIdleCallback(
() => {
this.assignDiscussionsToDiff(this.discussionsStructuredByLineCode);
this.assignDiscussionsToDiff();
},
{ timeout: 1000 },
);
......
......@@ -5,7 +5,6 @@ import createFlash from '~/flash';
import { s__ } from '~/locale';
import { handleLocationHash, historyPushState } from '~/lib/utils/common_utils';
import { mergeUrlParams, getLocationHash } from '~/lib/utils/url_utility';
import { reduceDiscussionsToLineCodes } from '../../notes/stores/utils';
import { getDiffPositionByLineCode, getNoteFormData } from './utils';
import * as types from './mutation_types';
import {
......@@ -36,18 +35,17 @@ export const fetchDiffFiles = ({ state, commit }) => {
// This is adding line discussions to the actual lines in the diff tree
// once for parallel and once for inline mode
export const assignDiscussionsToDiff = ({ state, commit }, allLineDiscussions) => {
export const assignDiscussionsToDiff = (
{ commit, state, rootState },
discussions = rootState.notes.discussions,
) => {
const diffPositionByLineCode = getDiffPositionByLineCode(state.diffFiles);
Object.values(allLineDiscussions).forEach(discussions => {
if (discussions.length > 0) {
const { fileHash } = discussions[0];
commit(types.SET_LINE_DISCUSSIONS_FOR_FILE, {
fileHash,
discussions,
diffPositionByLineCode,
});
}
discussions.filter(discussion => discussion.diff_discussion).forEach(discussion => {
commit(types.SET_LINE_DISCUSSIONS_FOR_FILE, {
discussion,
diffPositionByLineCode,
});
});
};
......@@ -190,9 +188,7 @@ export const saveDiffDiscussion = ({ dispatch }, { note, formData }) => {
return dispatch('saveNote', postData, { root: true })
.then(result => dispatch('updateDiscussion', result.discussion, { root: true }))
.then(discussion =>
dispatch('assignDiscussionsToDiff', reduceDiscussionsToLineCodes([discussion])),
)
.then(discussion => dispatch('assignDiscussionsToDiff', [discussion]))
.catch(() => createFlash(s__('MergeRequests|Saving the comment failed')));
};
......
......@@ -90,53 +90,67 @@ export default {
}));
},
[types.SET_LINE_DISCUSSIONS_FOR_FILE](state, { fileHash, discussions, diffPositionByLineCode }) {
const selectedFile = state.diffFiles.find(f => f.fileHash === fileHash);
const firstDiscussion = discussions[0];
const isDiffDiscussion = firstDiscussion.diff_discussion;
const hasLineCode = firstDiscussion.line_code;
const diffPosition = diffPositionByLineCode[firstDiscussion.line_code];
if (
selectedFile &&
isDiffDiscussion &&
hasLineCode &&
diffPosition &&
[types.SET_LINE_DISCUSSIONS_FOR_FILE](state, { discussion, diffPositionByLineCode }) {
const { latestDiff } = state;
const discussionLineCode = discussion.line_code;
const fileHash = discussion.diff_file.file_hash;
const lineCheck = ({ lineCode }) =>
lineCode === discussionLineCode &&
isDiscussionApplicableToLine({
discussion: firstDiscussion,
diffPosition,
latestDiff: state.latestDiff,
})
) {
const targetLine = selectedFile.parallelDiffLines.find(
line =>
(line.left && line.left.lineCode === firstDiscussion.line_code) ||
(line.right && line.right.lineCode === firstDiscussion.line_code),
);
if (targetLine) {
if (targetLine.left && targetLine.left.lineCode === firstDiscussion.line_code) {
Object.assign(targetLine.left, {
discussions,
});
} else {
Object.assign(targetLine.right, {
discussions,
discussion,
diffPosition: diffPositionByLineCode[lineCode],
latestDiff,
});
state.diffFiles = state.diffFiles.map(diffFile => {
if (diffFile.fileHash === fileHash) {
const file = { ...diffFile };
if (file.highlightedDiffLines) {
file.highlightedDiffLines = file.highlightedDiffLines.map(line => {
if (lineCheck(line)) {
return {
...line,
discussions: line.discussions.concat(discussion),
};
}
return line;
});
}
}
if (selectedFile.highlightedDiffLines) {
const targetInlineLine = selectedFile.highlightedDiffLines.find(
line => line.lineCode === firstDiscussion.line_code,
);
if (targetInlineLine) {
Object.assign(targetInlineLine, {
discussions,
if (file.parallelDiffLines) {
file.parallelDiffLines = file.parallelDiffLines.map(line => {
const left = line.left && lineCheck(line.left);
const right = line.right && lineCheck(line.right);
if (left || right) {
return {
left: {
...line.left,
discussions: left ? line.left.discussions.concat(discussion) : [],
},
right: {
...line.right,
discussions: right ? line.right.discussions.concat(discussion) : [],
},
};
}
return line;
});
}
if (!file.parallelDiffLines || !file.highlightedDiffLines) {
file.discussions = file.discussions.concat(discussion);
}
return file;
}
}
return diffFile;
});
},
[types.REMOVE_LINE_DISCUSSIONS_FOR_FILE](state, { fileHash, lineCode }) {
......
......@@ -31,7 +31,7 @@ export default {
},
},
computed: {
...mapState(['job', 'stages', 'jobs', 'selectedStage']),
...mapState(['job', 'stages', 'jobs', 'selectedStage', 'isLoadingStages']),
coverage() {
return `${this.job.coverage}%`;
},
......@@ -59,10 +59,10 @@ export default {
return '';
}
let t = this.job.metadata.timeout_human_readable;
if (this.job.metadata.timeout_source !== '') {
t += ` (from ${this.job.metadata.timeout_source})`;
}
let t = this.job.metadata.timeout_human_readable;
if (this.job.metadata.timeout_source !== '') {
t += ` (from ${this.job.metadata.timeout_source})`;
}
return t;
},
......@@ -270,6 +270,7 @@ export default {
/>
<stages-dropdown
v-if="!isLoadingStages"
:stages="stages"
:pipeline="job.pipeline"
:selected-stage="selectedStage"
......
......@@ -22,7 +22,6 @@ export default {
required: true,
},
},
computed: {
hasRef() {
return !_.isEmpty(this.pipeline.ref);
......
......@@ -71,7 +71,7 @@ export default {
* after the first request,
* and we do not want to hijack that
*/
if (state.selectedStage === 'More' && job.stage) {
if (state.selectedStage === '' && job.stage) {
state.selectedStage = job.stage;
}
},
......
import { __ } from '~/locale';
export default () => ({
jobEndpoint: null,
traceEndpoint: null,
......@@ -29,7 +27,7 @@ export default () => ({
// sidebar dropdown & list of jobs
isLoadingStages: false,
isLoadingJobs: false,
selectedStage: __('More'),
selectedStage: '',
stages: [],
jobs: [],
});
......@@ -473,3 +473,15 @@ export const stringifyTime = timeObject => {
*/
export const abbreviateTime = timeStr =>
timeStr.split(' ').filter(unitStr => unitStr.charAt(0) !== '0')[0];
/**
* Calculates the milliseconds between now and a given date string.
* The result cannot become negative.
*
* @param endDate date string that the time difference is calculated for
* @return {number} number of milliseconds remaining until the given date
*/
export const calculateRemainingMilliseconds = endDate => {
const remainingMilliseconds = new Date(endDate).getTime() - Date.now();
return Math.max(remainingMilliseconds, 0);
};
......@@ -2,6 +2,8 @@
import $ from 'jquery';
import { insertText } from '~/lib/utils/common_utils';
const LINK_TAG_PATTERN = '[{text}](url)';
function selectedText(text, textarea) {
return text.substring(textarea.selectionStart, textarea.selectionEnd);
}
......@@ -76,6 +78,21 @@ export function insertMarkdownText({ textArea, text, tag, blockTag, selected, wr
removedFirstNewLine = false;
currentLineEmpty = false;
// check for link pattern and selected text is an URL
// if so fill in the url part instead of the text part of the pattern.
if (tag === LINK_TAG_PATTERN) {
if (URL) {
try {
const ignoredUrl = new URL(selected);
// valid url
tag = '[text]({text})';
select = 'text';
} catch (e) {
// ignore - no valid url
}
}
}
// Remove the first newline
if (selected.indexOf('\n') === 0) {
removedFirstNewLine = true;
......
......@@ -175,6 +175,7 @@ export default {
this.state = 'noData';
return;
}
this.showEmptyState = false;
})
.then(this.resize)
......@@ -216,7 +217,10 @@ export default {
name="chevron-down"
/>
</button>
<div class="dropdown-menu dropdown-menu-selectable dropdown-menu-drop-up">
<div
v-if="store.environmentsData.length > 0"
class="dropdown-menu dropdown-menu-selectable dropdown-menu-drop-up"
>
<ul>
<li
v-for="environment in store.environmentsData"
......
......@@ -121,6 +121,7 @@ export default {
draw() {
const breakpointSize = bp.getBreakpointSize();
const query = this.graphData.queries[0];
const svgWidth = this.$refs.baseSvg.getBoundingClientRect().width;
this.margin = measurements.large.margin;
if (this.smallGraph || breakpointSize === 'xs' || breakpointSize === 'sm') {
this.graphHeight = 300;
......@@ -130,13 +131,13 @@ export default {
this.unitOfDisplay = query.unit || '';
this.yAxisLabel = this.graphData.y_label || 'Values';
this.legendTitle = query.label || 'Average';
this.graphWidth = this.$refs.baseSvg.clientWidth - this.margin.left - this.margin.right;
this.graphWidth = svgWidth - this.margin.left - this.margin.right;
this.graphHeight = this.graphHeight - this.margin.top - this.margin.bottom;
this.baseGraphHeight = this.graphHeight - 50;
this.baseGraphWidth = this.graphWidth;
// pixel offsets inside the svg and outside are not 1:1
this.realPixelRatio = this.$refs.baseSvg.clientWidth / this.baseGraphWidth;
this.realPixelRatio = svgWidth / this.baseGraphWidth;
this.renderAxesPaths();
this.formatDeployments();
......
......@@ -95,29 +95,20 @@ export default {
return awardList.filter(award => award.user.id === this.getUserData.id).length;
},
awardTitle(awardsList) {
const hasReactionByCurrentUser = this.hasReactionByCurrentUser(
awardsList,
);
const hasReactionByCurrentUser = this.hasReactionByCurrentUser(awardsList);
const TOOLTIP_NAME_COUNT = hasReactionByCurrentUser ? 9 : 10;
let awardList = awardsList;
// Filter myself from list if I am awarded.
if (hasReactionByCurrentUser) {
awardList = awardList.filter(
award => award.user.id !== this.getUserData.id,
);
awardList = awardList.filter(award => award.user.id !== this.getUserData.id);
}
// Get only 9-10 usernames to show in tooltip text.
const namesToShow = awardList
.slice(0, TOOLTIP_NAME_COUNT)
.map(award => award.user.name);
const namesToShow = awardList.slice(0, TOOLTIP_NAME_COUNT).map(award => award.user.name);
// Get the remaining list to use in `and x more` text.
const remainingAwardList = awardList.slice(
TOOLTIP_NAME_COUNT,
awardList.length,
);
const remainingAwardList = awardList.slice(TOOLTIP_NAME_COUNT, awardList.length);
// Add myself to the begining of the list so title will start with You.
if (hasReactionByCurrentUser) {
......@@ -128,9 +119,7 @@ export default {
// We have 10+ awarded user, join them with comma and add `and x more`.
if (remainingAwardList.length) {
title = `${namesToShow.join(', ')}, and ${
remainingAwardList.length
} more.`;
title = `${namesToShow.join(', ')}, and ${remainingAwardList.length} more.`;
} else if (namesToShow.length > 1) {
// Join all names with comma but not the last one, it will be added with and text.
title = namesToShow.slice(0, namesToShow.length - 1).join(', ');
......@@ -170,9 +159,7 @@ export default {
awardName: parsedName,
};
this.toggleAwardRequest(data).catch(() =>
Flash('Something went wrong on our end.'),
);
this.toggleAwardRequest(data).catch(() => Flash('Something went wrong on our end.'));
},
},
};
......
......@@ -68,10 +68,7 @@ export const collapseSystemNotes = notes => {
lastDescriptionSystemNote = note;
lastDescriptionSystemNoteIndex = acc.length;
} else if (lastDescriptionSystemNote) {
const timeDifferenceMinutes = getTimeDifferenceMinutes(
lastDescriptionSystemNote,
note,
);
const timeDifferenceMinutes = getTimeDifferenceMinutes(lastDescriptionSystemNote, note);
// are they less than 10 minutes appart?
if (timeDifferenceMinutes > 10) {
......
import _ from 'underscore';
import * as constants from '../constants';
import { reduceDiscussionsToLineCodes } from './utils';
import { collapseSystemNotes } from './collapse_utils';
export const discussions = state => collapseSystemNotes(state.discussions);
......@@ -31,9 +30,6 @@ export const notesById = state =>
return acc;
}, {});
export const discussionsStructuredByLineCode = state =>
reduceDiscussionsToLineCodes(state.discussions);
export const noteableType = state => {
const { ISSUE_NOTEABLE_TYPE, MERGE_REQUEST_NOTEABLE_TYPE, EPIC_NOTEABLE_TYPE } = constants;
......
......@@ -4,5 +4,4 @@ import notesModule from './modules';
Vue.use(Vuex);
export default () =>
new Vuex.Store(notesModule());
export default () => new Vuex.Store(notesModule());
......@@ -25,18 +25,6 @@ export const getQuickActionText = note => {
return text;
};
export const reduceDiscussionsToLineCodes = selectedDiscussions =>
selectedDiscussions.reduce((acc, note) => {
if (note.diff_discussion && note.line_code) {
// For context about line notes: there might be multiple notes with the same line code
const items = acc[note.line_code] || [];
items.push(note);
Object.assign(acc, { [note.line_code]: items });
}
return acc;
}, {});
export const hasQuickActions = note => REGEX_QUICK_ACTIONS.test(note);
export const stripQuickActions = note => note.replace(REGEX_QUICK_ACTIONS, '').trim();
......@@ -31,7 +31,7 @@ export default class AbuseReports {
$messageCellElement.text(originalMessage);
} else {
$messageCellElement.data('messageTruncated', 'true');
$messageCellElement.text(`${originalMessage.substr(0, (MAX_MESSAGE_LENGTH - 3))}...`);
$messageCellElement.text(`${originalMessage.substr(0, MAX_MESSAGE_LENGTH - 3)}...`);
}
}
}
......@@ -23,7 +23,7 @@ export default function adminInit() {
}
});
$('body').on('click', '.js-toggle-colors-link', (e) => {
$('body').on('click', '.js-toggle-colors-link', e => {
e.preventDefault();
$('.js-toggle-colors-container').toggleClass('hide');
});
......@@ -33,12 +33,15 @@ export default function adminInit() {
$(this).tab('show');
});
$('.log-bottom').on('click', (e) => {
$('.log-bottom').on('click', e => {
e.preventDefault();
const $visibleLog = $('.file-content:visible');
$visibleLog.animate({
scrollTop: $visibleLog.find('ol').height(),
}, 'fast');
$visibleLog.animate(
{
scrollTop: $visibleLog.find('ol').height(),
},
'fast',
);
});
$('.change-owner-link').on('click', function changeOwnerLinkClick(e) {
......@@ -47,7 +50,7 @@ export default function adminInit() {
modal.show();
});
$('.change-owner-cancel-link').on('click', (e) => {
$('.change-owner-cancel-link').on('click', e => {
e.preventDefault();
modal.hide();
$('.change-owner-link').show();
......
import { __ } from '~/locale';
export const PLACEHOLDER_USER_EXTERNAL_DEFAULT_TRUE = __('Regex pattern');
export const PLACEHOLDER_USER_EXTERNAL_DEFAULT_FALSE = __('To define internal users, first enable new users set to external');
export const PLACEHOLDER_USER_EXTERNAL_DEFAULT_FALSE = __(
'To define internal users, first enable new users set to external',
);
function setUserInternalRegexPlaceholder(checkbox) {
const userInternalRegex = document.getElementById('application_setting_user_default_internal_regex');
const userInternalRegex = document.getElementById(
'application_setting_user_default_internal_regex',
);
if (checkbox && userInternalRegex) {
if (checkbox.checked) {
userInternalRegex.readOnly = false;
......
......@@ -17,20 +17,24 @@ export default () => {
const previewPath = $('textarea#broadcast_message_message').data('previewPath');
$('textarea#broadcast_message_message').on('input', _.debounce(function onMessageInput() {
const message = $(this).val();
if (message === '') {
$('.js-broadcast-message-preview').text('Your message here');
} else {
axios.post(previewPath, {
broadcast_message: {
message,
},
})
.then(({ data }) => {
$('.js-broadcast-message-preview').html(data.message);
})
.catch(() => flash(__('An error occurred while rendering preview broadcast message')));
}
}, 250));
$('textarea#broadcast_message_message').on(
'input',
_.debounce(function onMessageInput() {
const message = $(this).val();
if (message === '') {
$('.js-broadcast-message-preview').text('Your message here');
} else {
axios
.post(previewPath, {
broadcast_message: {
message,
},
})
.then(({ data }) => {
$('.js-broadcast-message-preview').html(data.message);
})
.catch(() => flash(__('An error occurred while rendering preview broadcast message')));
}
}, 250),
);
};
<script>
import axios from '~/lib/utils/axios_utils';
import createFlash from '~/flash';
import GlModal from '~/vue_shared/components/gl_modal.vue';
import { redirectTo } from '~/lib/utils/url_utility';
import { s__ } from '~/locale';
import axios from '~/lib/utils/axios_utils';
import createFlash from '~/flash';
import GlModal from '~/vue_shared/components/gl_modal.vue';
import { redirectTo } from '~/lib/utils/url_utility';
import { s__ } from '~/locale';
export default {
components: {
GlModal,
export default {
components: {
GlModal,
},
props: {
url: {
type: String,
required: true,
},
props: {
url: {
type: String,
required: true,
},
},
computed: {
text() {
return s__(
'AdminArea|You’re about to stop all jobs.This will halt all current jobs that are running.',
);
},
computed: {
text() {
return s__('AdminArea|You’re about to stop all jobs.This will halt all current jobs that are running.');
},
},
methods: {
onSubmit() {
return axios
.post(this.url)
.then(response => {
// follow the rediect to refresh the page
redirectTo(response.request.responseURL);
})
.catch(error => {
createFlash(s__('AdminArea|Stopping jobs failed'));
throw error;
});
},
methods: {
onSubmit() {
return axios.post(this.url)
.then((response) => {
// follow the rediect to refresh the page
redirectTo(response.request.responseURL);
})
.catch((error) => {
createFlash(s__('AdminArea|Stopping jobs failed'));
throw error;
});
},
},
};
},
};
</script>
<template>
......
......@@ -4,6 +4,7 @@ import NamespaceSelect from '../../../namespace_select';
document.addEventListener('DOMContentLoaded', () => {
new ProjectsList(); // eslint-disable-line no-new
document.querySelectorAll('.js-namespace-select')
document
.querySelectorAll('.js-namespace-select')
.forEach(dropdown => new NamespaceSelect({ dropdown }));
});
<script>
import _ from 'underscore';
import DeprecatedModal from '~/vue_shared/components/deprecated_modal.vue';
import { s__, sprintf } from '~/locale';
import _ from 'underscore';
import DeprecatedModal from '~/vue_shared/components/deprecated_modal.vue';
import { s__, sprintf } from '~/locale';
export default {
components: {
DeprecatedModal,
export default {
components: {
DeprecatedModal,
},
props: {
deleteProjectUrl: {
type: String,
required: false,
default: '',
},
props: {
deleteProjectUrl: {
type: String,
required: false,
default: '',
},
projectName: {
type: String,
required: false,
default: '',
},
csrfToken: {
type: String,
required: false,
default: '',
},
projectName: {
type: String,
required: false,
default: '',
},
data() {
return {
enteredProjectName: '',
};
csrfToken: {
type: String,
required: false,
default: '',
},
computed: {
title() {
return sprintf(s__('AdminProjects|Delete Project %{projectName}?'),
{
projectName: `'${_.escape(this.projectName)}'`,
},
false,
);
},
text() {
return sprintf(s__(`AdminProjects|
},
data() {
return {
enteredProjectName: '',
};
},
computed: {
title() {
return sprintf(
s__('AdminProjects|Delete Project %{projectName}?'),
{
projectName: `'${_.escape(this.projectName)}'`,
},
false,
);
},
text() {
return sprintf(
s__(`AdminProjects|
You’re about to permanently delete the project %{projectName}, its repository,
and all related resources including issues, merge requests, etc.. Once you confirm and press
%{strong_start}Delete project%{strong_end}, it cannot be undone or recovered.`),
{
projectName: `<strong>${_.escape(this.projectName)}</strong>`,
strong_start: '<strong>',
strong_end: '</strong>',
},
false,
);
},
confirmationTextLabel() {
return sprintf(s__('AdminUsers|To confirm, type %{projectName}'),
{
projectName: `<code>${_.escape(this.projectName)}</code>`,
},
false,
);
},
primaryButtonLabel() {
return s__('AdminProjects|Delete project');
},
canSubmit() {
return this.enteredProjectName === this.projectName;
},
{
projectName: `<strong>${_.escape(this.projectName)}</strong>`,
strong_start: '<strong>',
strong_end: '</strong>',
},
false,
);
},
confirmationTextLabel() {
return sprintf(
s__('AdminUsers|To confirm, type %{projectName}'),
{
projectName: `<code>${_.escape(this.projectName)}</code>`,
},
false,
);
},
primaryButtonLabel() {
return s__('AdminProjects|Delete project');
},
canSubmit() {
return this.enteredProjectName === this.projectName;
},
},
methods: {
onCancel() {
this.enteredProjectName = '';
},
methods: {
onCancel() {
this.enteredProjectName = '';
},
onSubmit() {
this.$refs.form.submit();
this.enteredProjectName = '';
},
onSubmit() {
this.$refs.form.submit();
this.enteredProjectName = '';
},
};
},
};
</script>
<template>
......
......@@ -28,7 +28,7 @@ document.addEventListener('DOMContentLoaded', () => {
},
});
$(document).on('shown.bs.modal', (event) => {
$(document).on('shown.bs.modal', event => {
if (event.relatedTarget.classList.contains('delete-project-button')) {
const buttonProps = event.relatedTarget.dataset;
deleteModal.deleteProjectUrl = buttonProps.deleteProjectUrl;
......
<script>
import _ from 'underscore';
import DeprecatedModal from '~/vue_shared/components/deprecated_modal.vue';
import { s__, sprintf } from '~/locale';
import _ from 'underscore';
import DeprecatedModal from '~/vue_shared/components/deprecated_modal.vue';
import { s__, sprintf } from '~/locale';
export default {
components: {
DeprecatedModal,
export default {
components: {
DeprecatedModal,
},
props: {
deleteUserUrl: {
type: String,
required: false,
default: '',
},
props: {
deleteUserUrl: {
type: String,
required: false,
default: '',
},
blockUserUrl: {
type: String,
required: false,
default: '',
},
deleteContributions: {
type: Boolean,
required: false,
default: false,
},
username: {
type: String,
required: false,
default: '',
},
csrfToken: {
type: String,
required: false,
default: '',
},
blockUserUrl: {
type: String,
required: false,
default: '',
},
data() {
return {
enteredUsername: '',
};
deleteContributions: {
type: Boolean,
required: false,
default: false,
},
computed: {
title() {
const keepContributionsTitle = s__('AdminUsers|Delete User %{username}?');
const deleteContributionsTitle = s__('AdminUsers|Delete User %{username} and contributions?');
username: {
type: String,
required: false,
default: '',
},
csrfToken: {
type: String,
required: false,
default: '',
},
},
data() {
return {
enteredUsername: '',
};
},
computed: {
title() {
const keepContributionsTitle = s__('AdminUsers|Delete User %{username}?');
const deleteContributionsTitle = s__('AdminUsers|Delete User %{username} and contributions?');
return sprintf(
this.deleteContributions ? deleteContributionsTitle : keepContributionsTitle, {
username: `'${_.escape(this.username)}'`,
}, false);
},
text() {
const keepContributionsText = s__(`AdminArea|
return sprintf(
this.deleteContributions ? deleteContributionsTitle : keepContributionsTitle,
{
username: `'${_.escape(this.username)}'`,
},
false,
);
},
text() {
const keepContributionsText = s__(`AdminArea|
You are about to permanently delete the user %{username}.
Issues, merge requests, and groups linked to them will be transferred to a system-wide "Ghost-user".
To avoid data loss, consider using the %{strong_start}block user%{strong_end} feature instead.
Once you %{strong_start}Delete user%{strong_end}, it cannot be undone or recovered.`);
const deleteContributionsText = s__(`AdminArea|
const deleteContributionsText = s__(`AdminArea|
You are about to permanently delete the user %{username}.
This will delete all of the issues, merge requests, and groups linked to them.
To avoid data loss, consider using the %{strong_start}block user%{strong_end} feature instead.
Once you %{strong_start}Delete user%{strong_end}, it cannot be undone or recovered.`);
return sprintf(this.deleteContributions ? deleteContributionsText : keepContributionsText,
{
username: `<strong>${_.escape(this.username)}</strong>`,
strong_start: '<strong>',
strong_end: '</strong>',
},
false,
);
},
confirmationTextLabel() {
return sprintf(s__('AdminUsers|To confirm, type %{username}'),
{
username: `<code>${_.escape(this.username)}</code>`,
},
false,
);
},
primaryButtonLabel() {
const keepContributionsLabel = s__('AdminUsers|Delete user');
const deleteContributionsLabel = s__('AdminUsers|Delete user and contributions');
return sprintf(
this.deleteContributions ? deleteContributionsText : keepContributionsText,
{
username: `<strong>${_.escape(this.username)}</strong>`,
strong_start: '<strong>',
strong_end: '</strong>',
},
false,
);
},
confirmationTextLabel() {
return sprintf(
s__('AdminUsers|To confirm, type %{username}'),
{
username: `<code>${_.escape(this.username)}</code>`,
},
false,
);
},
primaryButtonLabel() {
const keepContributionsLabel = s__('AdminUsers|Delete user');
const deleteContributionsLabel = s__('AdminUsers|Delete user and contributions');
return this.deleteContributions ? deleteContributionsLabel : keepContributionsLabel;
},
secondaryButtonLabel() {
return s__('AdminUsers|Block user');
},
canSubmit() {
return this.enteredUsername === this.username;
},
return this.deleteContributions ? deleteContributionsLabel : keepContributionsLabel;
},
methods: {
onCancel() {
this.enteredUsername = '';
},
onSecondaryAction() {
const { form } = this.$refs;
secondaryButtonLabel() {
return s__('AdminUsers|Block user');
},
canSubmit() {
return this.enteredUsername === this.username;
},
},
methods: {
onCancel() {
this.enteredUsername = '';
},
onSecondaryAction() {
const { form } = this.$refs;
form.action = this.blockUserUrl;
this.$refs.method.value = 'put';
form.action = this.blockUserUrl;
this.$refs.method.value = 'put';
form.submit();
},
onSubmit() {
this.$refs.form.submit();
this.enteredUsername = '';
},
form.submit();
},
onSubmit() {
this.$refs.form.submit();
this.enteredUsername = '';
},
};
},
};
</script>
<template>
......
......@@ -32,12 +32,14 @@ document.addEventListener('DOMContentLoaded', () => {
},
});
$(document).on('shown.bs.modal', (event) => {
$(document).on('shown.bs.modal', event => {
if (event.relatedTarget.classList.contains('delete-user-button')) {
const buttonProps = event.relatedTarget.dataset;
deleteModal.deleteUserUrl = buttonProps.deleteUserUrl;
deleteModal.blockUserUrl = buttonProps.blockUserUrl;
deleteModal.deleteContributions = event.relatedTarget.hasAttribute('data-delete-contributions');
deleteModal.deleteContributions = event.relatedTarget.hasAttribute(
'data-delete-contributions',
);
deleteModal.username = buttonProps.username;
}
});
......
......@@ -4,7 +4,9 @@ export default class UserInternalRegexHandler {
constructor() {
this.regexPattern = $('[data-user-internal-regex-pattern]').data('user-internal-regex-pattern');
if (this.regexPattern && this.regexPattern !== '') {
this.regexOptions = $('[data-user-internal-regex-options]').data('user-internal-regex-options');
this.regexOptions = $('[data-user-internal-regex-options]').data(
'user-internal-regex-options',
);
this.external = $('#user_external');
this.warningMessage = $('#warning_external_automatically_set');
this.addListenerToEmailField();
......@@ -13,7 +15,7 @@ export default class UserInternalRegexHandler {
}
addListenerToEmailField() {
$('#user_email').on('input', (event) => {
$('#user_email').on('input', event => {
this.setExternalCheckbox(event.currentTarget.value);
});
}
......
......@@ -79,7 +79,8 @@ export default class Todos {
.then(({ data }) => {
this.updateRowState(target);
this.updateBadges(data);
}).catch(() => {
})
.catch(() => {
this.updateRowState(target, true);
return flash(__('Error updating todo status.'));
});
......@@ -118,10 +119,12 @@ export default class Todos {
axios[target.dataset.method](target.dataset.href, {
ids: this.todo_ids,
}).then(({ data }) => {
this.updateAllState(target, data);
this.updateBadges(data);
}).catch(() => flash(__('Error updating status for all todos.')));
})
.then(({ data }) => {
this.updateAllState(target, data);
this.updateBadges(data);
})
.catch(() => flash(__('Error updating status for all todos.')));
}
updateAllState(target, data) {
......@@ -133,7 +136,7 @@ export default class Todos {
target.removeAttribute('disabled');
target.classList.remove('disabled');
this.todo_ids = (target === markAllDoneBtn) ? data.updated_ids : [];
this.todo_ids = target === markAllDoneBtn ? data.updated_ids : [];
undoAllBtn.classList.toggle('hidden');
markAllDoneBtn.classList.toggle('hidden');
todoListContainer.classList.toggle('hidden');
......
<script>
import axios from '~/lib/utils/axios_utils';
import axios from '~/lib/utils/axios_utils';
import Flash from '~/flash';
import DeprecatedModal from '~/vue_shared/components/deprecated_modal.vue';
import { n__, s__, sprintf } from '~/locale';
import { redirectTo } from '~/lib/utils/url_utility';
import eventHub from '../event_hub';
import Flash from '~/flash';
import DeprecatedModal from '~/vue_shared/components/deprecated_modal.vue';
import { n__, s__, sprintf } from '~/locale';
import { redirectTo } from '~/lib/utils/url_utility';
import eventHub from '../event_hub';
export default {
components: {
DeprecatedModal,
export default {
components: {
DeprecatedModal,
},
props: {
issueCount: {
type: Number,
required: true,
},
props: {
issueCount: {
type: Number,
required: true,
},
mergeRequestCount: {
type: Number,
required: true,
},
milestoneId: {
type: Number,
required: true,
},
milestoneTitle: {
type: String,
required: true,
},
milestoneUrl: {
type: String,
required: true,
},
mergeRequestCount: {
type: Number,
required: true,
},
computed: {
text() {
const milestoneTitle = sprintf('<strong>%{milestoneTitle}</strong>', { milestoneTitle: this.milestoneTitle });
if (this.issueCount === 0 && this.mergeRequestCount === 0) {
return sprintf(
s__(`Milestones|
You’re about to permanently delete the milestone %{milestoneTitle}.
This milestone is not currently used in any issues or merge requests.`),
{
milestoneTitle,
},
false,
);
}
milestoneId: {
type: Number,
required: true,
},
milestoneTitle: {
type: String,
required: true,
},
milestoneUrl: {
type: String,
required: true,
},
},
computed: {
text() {
const milestoneTitle = sprintf('<strong>%{milestoneTitle}</strong>', {
milestoneTitle: this.milestoneTitle,
});
if (this.issueCount === 0 && this.mergeRequestCount === 0) {
return sprintf(
s__(`Milestones|
You’re about to permanently delete the milestone %{milestoneTitle} and remove it from %{issuesWithCount} and %{mergeRequestsWithCount}.
Once deleted, it cannot be undone or recovered.`),
You’re about to permanently delete the milestone %{milestoneTitle}.
This milestone is not currently used in any issues or merge requests.`),
{
milestoneTitle,
issuesWithCount: n__('%d issue', '%d issues', this.issueCount),
mergeRequestsWithCount: n__('%d merge request', '%d merge requests', this.mergeRequestCount),
},
false,
);
},
title() {
return sprintf(s__('Milestones|Delete milestone %{milestoneTitle}?'), { milestoneTitle: this.milestoneTitle });
},
},
methods: {
onSubmit() {
eventHub.$emit('deleteMilestoneModal.requestStarted', this.milestoneUrl);
}
return axios.delete(this.milestoneUrl)
.then((response) => {
eventHub.$emit('deleteMilestoneModal.requestFinished', { milestoneUrl: this.milestoneUrl, successful: true });
return sprintf(
s__(`Milestones|
You’re about to permanently delete the milestone %{milestoneTitle} and remove it from %{issuesWithCount} and %{mergeRequestsWithCount}.
Once deleted, it cannot be undone or recovered.`),
{
milestoneTitle,
issuesWithCount: n__('%d issue', '%d issues', this.issueCount),
mergeRequestsWithCount: n__(
'%d merge request',
'%d merge requests',
this.mergeRequestCount,
),
},
false,
);
},
title() {
return sprintf(s__('Milestones|Delete milestone %{milestoneTitle}?'), {
milestoneTitle: this.milestoneTitle,
});
},
},
methods: {
onSubmit() {
eventHub.$emit('deleteMilestoneModal.requestStarted', this.milestoneUrl);
// follow the rediect to milestones overview page
redirectTo(response.request.responseURL);
})
.catch((error) => {
eventHub.$emit('deleteMilestoneModal.requestFinished', { milestoneUrl: this.milestoneUrl, successful: false });
return axios
.delete(this.milestoneUrl)
.then(response => {
eventHub.$emit('deleteMilestoneModal.requestFinished', {
milestoneUrl: this.milestoneUrl,
successful: true,
});
if (error.response && error.response.status === 404) {
Flash(sprintf(s__('Milestones|Milestone %{milestoneTitle} was not found'), { milestoneTitle: this.milestoneTitle }));
} else {
Flash(sprintf(s__('Milestones|Failed to delete milestone %{milestoneTitle}'), { milestoneTitle: this.milestoneTitle }));
}
throw error;
// follow the rediect to milestones overview page
redirectTo(response.request.responseURL);
})
.catch(error => {
eventHub.$emit('deleteMilestoneModal.requestFinished', {
milestoneUrl: this.milestoneUrl,
successful: false,
});
},
if (error.response && error.response.status === 404) {
Flash(
sprintf(s__('Milestones|Milestone %{milestoneTitle} was not found'), {
milestoneTitle: this.milestoneTitle,
}),
);
} else {
Flash(
sprintf(s__('Milestones|Failed to delete milestone %{milestoneTitle}'), {
milestoneTitle: this.milestoneTitle,
}),
);
}
throw error;
});
},
};
},
};
</script>
<template>
......
......@@ -7,7 +7,9 @@ export default () => {
Vue.use(Translate);
const onRequestFinished = ({ milestoneUrl, successful }) => {
const button = document.querySelector(`.js-delete-milestone-button[data-milestone-url="${milestoneUrl}"]`);
const button = document.querySelector(
`.js-delete-milestone-button[data-milestone-url="${milestoneUrl}"]`,
);
if (!successful) {
button.removeAttribute('disabled');
......@@ -16,14 +18,16 @@ export default () => {
button.querySelector('.js-loading-icon').classList.add('hidden');
};
const onRequestStarted = (milestoneUrl) => {
const button = document.querySelector(`.js-delete-milestone-button[data-milestone-url="${milestoneUrl}"]`);
const onRequestStarted = milestoneUrl => {
const button = document.querySelector(
`.js-delete-milestone-button[data-milestone-url="${milestoneUrl}"]`,
);
button.setAttribute('disabled', '');
button.querySelector('.js-loading-icon').classList.remove('hidden');
eventHub.$once('deleteMilestoneModal.requestFinished', onRequestFinished);
};
const onDeleteButtonClick = (event) => {
const onDeleteButtonClick = event => {
const button = event.currentTarget;
const modalProps = {
milestoneId: parseInt(button.dataset.milestoneId, 10),
......@@ -37,12 +41,12 @@ export default () => {
};
const deleteMilestoneButtons = document.querySelectorAll('.js-delete-milestone-button');
deleteMilestoneButtons.forEach((button) => {
deleteMilestoneButtons.forEach(button => {
button.addEventListener('click', onDeleteButtonClick);
});
eventHub.$once('deleteMilestoneModal.mounted', () => {
deleteMilestoneButtons.forEach((button) => {
deleteMilestoneButtons.forEach(button => {
button.removeAttribute('disabled');
});
});
......
......@@ -7,20 +7,24 @@ Vue.use(Translate);
export default () => {
const onRequestFinished = ({ milestoneUrl, successful }) => {
const button = document.querySelector(`.js-promote-project-milestone-button[data-url="${milestoneUrl}"]`);
const button = document.querySelector(
`.js-promote-project-milestone-button[data-url="${milestoneUrl}"]`,
);
if (!successful) {
button.removeAttribute('disabled');
}
};
const onRequestStarted = (milestoneUrl) => {
const button = document.querySelector(`.js-promote-project-milestone-button[data-url="${milestoneUrl}"]`);
const onRequestStarted = milestoneUrl => {
const button = document.querySelector(
`.js-promote-project-milestone-button[data-url="${milestoneUrl}"]`,
);
button.setAttribute('disabled', '');
eventHub.$once('promoteMilestoneModal.requestFinished', onRequestFinished);
};
const onDeleteButtonClick = (event) => {
const onDeleteButtonClick = event => {
const button = event.currentTarget;
const modalProps = {
milestoneTitle: button.dataset.milestoneTitle,
......@@ -32,12 +36,12 @@ export default () => {
};
const promoteMilestoneButtons = document.querySelectorAll('.js-promote-project-milestone-button');
promoteMilestoneButtons.forEach((button) => {
promoteMilestoneButtons.forEach(button => {
button.addEventListener('click', onDeleteButtonClick);
});
eventHub.$once('promoteMilestoneModal.mounted', () => {
promoteMilestoneButtons.forEach((button) => {
promoteMilestoneButtons.forEach(button => {
button.removeAttribute('disabled');
});
});
......
......@@ -3,9 +3,12 @@ import '~/profile/gl_crop';
import Profile from '~/profile/profile';
document.addEventListener('DOMContentLoaded', () => {
$(document).on('input.ssh_key', '#key_key', function () { // eslint-disable-line func-names
// eslint-disable-next-line func-names
$(document).on('input.ssh_key', '#key_key', function() {
const $title = $('#key_title');
const comment = $(this).val().match(/^\S+ \S+ (.+)\n?$/);
const comment = $(this)
.val()
.match(/^\S+ \S+ (.+)\n?$/);
// Extract the SSH Key title from its comment
if (comment && comment.length > 1) {
......
......@@ -5,7 +5,9 @@ document.addEventListener('DOMContentLoaded', () => {
const twoFactorNode = document.querySelector('.js-two-factor-auth');
const skippable = twoFactorNode.dataset.twoFactorSkippable === 'true';
if (skippable) {
const button = `<a class="btn btn-sm btn-warning float-right" data-method="patch" href="${twoFactorNode.dataset.two_factor_skip_url}">Configure it later</a>`;
const button = `<a class="btn btn-sm btn-warning float-right" data-method="patch" href="${
twoFactorNode.dataset.two_factor_skip_url
}">Configure it later</a>`;
const flashAlert = document.querySelector('.flash-alert .container-fluid');
if (flashAlert) flashAlert.insertAdjacentHTML('beforeend', button);
}
......
import $ from 'jquery';
import NewBranchForm from '~/new_branch_form';
document.addEventListener('DOMContentLoaded', () => (
new NewBranchForm($('.js-create-branch-form'), JSON.parse(document.getElementById('availableRefs').innerHTML))
));
document.addEventListener(
'DOMContentLoaded',
() =>
new NewBranchForm(
$('.js-create-branch-form'),
JSON.parse(document.getElementById('availableRefs').innerHTML),
),
);
......@@ -31,14 +31,16 @@ document.addEventListener('DOMContentLoaded', () => {
const chartData = data => ({
labels: Object.keys(data),
datasets: [{
fillColor: 'rgba(220,220,220,0.5)',
strokeColor: 'rgba(220,220,220,1)',
barStrokeWidth: 1,
barValueSpacing: 1,
barDatasetSpacing: 1,
data: _.values(data),
}],
datasets: [
{
fillColor: 'rgba(220,220,220,0.5)',
strokeColor: 'rgba(220,220,220,1)',
barStrokeWidth: 1,
barValueSpacing: 1,
barDatasetSpacing: 1,
data: _.values(data),
},
],
});
const hourData = chartData(projectChartData.hour);
......@@ -51,7 +53,9 @@ document.addEventListener('DOMContentLoaded', () => {
responsiveChart($('#month-chart'), monthData);
const data = projectChartData.languages;
const ctx = $('#languages-chart').get(0).getContext('2d');
const ctx = $('#languages-chart')
.get(0)
.getContext('2d');
const options = {
scaleOverlay: true,
responsive: true,
......
......@@ -7,7 +7,8 @@ import ContributorsStatGraph from './stat_graph_contributors';
document.addEventListener('DOMContentLoaded', () => {
const url = document.querySelector('.js-graphs-show').dataset.projectGraphPath;
axios.get(url)
axios
.get(url)
.then(({ data }) => {
const graph = new ContributorsStatGraph();
graph.init(data);
......
......@@ -3,7 +3,11 @@
import $ from 'jquery';
import _ from 'underscore';
import { n__, s__, createDateTimeFormat, sprintf } from '~/locale';
import { ContributorsGraph, ContributorsAuthorGraph, ContributorsMasterGraph } from './stat_graph_contributors_graph';
import {
ContributorsGraph,
ContributorsAuthorGraph,
ContributorsMasterGraph,
} from './stat_graph_contributors_graph';
import ContributorsStatGraphUtil from './stat_graph_contributors_util';
export default (function() {
......@@ -14,7 +18,7 @@ export default (function() {
ContributorsStatGraph.prototype.init = function(log) {
var author_commits, total_commits;
this.parsed_log = ContributorsStatGraphUtil.parse_log(log);
this.set_current_field("commits");
this.set_current_field('commits');
total_commits = ContributorsStatGraphUtil.get_total_data(this.parsed_log, this.field);
author_commits = ContributorsStatGraphUtil.get_author_data(this.parsed_log, this.field);
this.add_master_graph(total_commits);
......@@ -31,23 +35,26 @@ export default (function() {
var limited_author_data;
this.authors = [];
limited_author_data = author_data.slice(0, 100);
return _.each(limited_author_data, (function(_this) {
return function(d) {
var author_graph, author_header;
author_header = _this.create_author_header(d);
$(".contributors-list").append(author_header);
author_graph = new ContributorsAuthorGraph(d.dates);
_this.authors[d.author_name] = author_graph;
return author_graph.draw();
};
})(this));
return _.each(
limited_author_data,
(function(_this) {
return function(d) {
var author_graph, author_header;
author_header = _this.create_author_header(d);
$('.contributors-list').append(author_header);
author_graph = new ContributorsAuthorGraph(d.dates);
_this.authors[d.author_name] = author_graph;
return author_graph.draw();
};
})(this),
);
};
ContributorsStatGraph.prototype.format_author_commit_info = function(author) {
var commits;
commits = $('<span/>', {
"class": 'graph-author-commits-count'
class: 'graph-author-commits-count',
});
commits.text(n__('%d commit', '%d commits', author.commits));
return $('<span/>').append(commits);
......@@ -56,13 +63,13 @@ export default (function() {
ContributorsStatGraph.prototype.create_author_header = function(author) {
var author_commit_info, author_commit_info_span, author_email, author_name, list_item;
list_item = $('<li/>', {
"class": 'person',
style: 'display: block;'
class: 'person',
style: 'display: block;',
});
author_name = $('<h4>' + author.author_name + '</h4>');
author_email = $('<p class="graph-author-email">' + author.author_email + '</p>');
author_commit_info_span = $('<span/>', {
"class": 'commits'
class: 'commits',
});
author_commit_info = this.format_author_commit_info(author);
author_commit_info_span.html(author_commit_info);
......@@ -80,37 +87,41 @@ export default (function() {
};
ContributorsStatGraph.prototype.redraw_authors = function() {
$("ol").html("");
$('ol').html('');
const { x_domain } = ContributorsGraph.prototype;
const author_commits = ContributorsStatGraphUtil.get_author_data(this.parsed_log, this.field, x_domain);
return _.each(author_commits, (function(_this) {
return function(d) {
_this.redraw_author_commit_info(d);
if (_this.authors[d.author_name] != null) {
$(_this.authors[d.author_name].list_item).appendTo("ol");
_this.authors[d.author_name].set_data(d.dates);
return _this.authors[d.author_name].redraw();
}
return '';
};
})(this));
const author_commits = ContributorsStatGraphUtil.get_author_data(
this.parsed_log,
this.field,
x_domain,
);
return _.each(
author_commits,
(function(_this) {
return function(d) {
_this.redraw_author_commit_info(d);
if (_this.authors[d.author_name] != null) {
$(_this.authors[d.author_name].list_item).appendTo('ol');
_this.authors[d.author_name].set_data(d.dates);
return _this.authors[d.author_name].redraw();
}
return '';
};
})(this),
);
};
ContributorsStatGraph.prototype.set_current_field = function(field) {
return this.field = field;
return (this.field = field);
};
ContributorsStatGraph.prototype.change_date_header = function() {
const { x_domain } = ContributorsGraph.prototype;
const formattedDateRange = sprintf(
s__('ContributorsPage|%{startDate} – %{endDate}'),
{
startDate: this.dateFormat.format(new Date(x_domain[0])),
endDate: this.dateFormat.format(new Date(x_domain[1])),
},
);
const formattedDateRange = sprintf(s__('ContributorsPage|%{startDate} – %{endDate}'), {
startDate: this.dateFormat.format(new Date(x_domain[0])),
endDate: this.dateFormat.format(new Date(x_domain[1])),
});
return $('#date_header').text(formattedDateRange);
};
......@@ -120,7 +131,7 @@ export default (function() {
if ($author != null) {
author_list_item = $(this.authors[author.author_name].list_item);
author_commit_info = this.format_author_commit_info(author);
return author_list_item.find("span").html(author_commit_info);
return author_list_item.find('span').html(author_commit_info);
}
return '';
};
......
......@@ -26,12 +26,12 @@ export default {
by_author = _.toArray(by_author);
return {
total: total,
by_author: by_author
by_author: by_author,
};
},
add_date: function(date, collection) {
collection[date] = {};
return collection[date].date = date;
return (collection[date].date = date);
},
add_author: function(author, by_author, by_email) {
var data, normalized_email;
......@@ -49,28 +49,28 @@ export default {
return this.store_deletions(entry, total, by_author);
},
store_commits: function(total, by_author) {
this.add(total, "commits", 1);
return this.add(by_author, "commits", 1);
this.add(total, 'commits', 1);
return this.add(by_author, 'commits', 1);
},
add: function(collection, field, value) {
if (collection[field] == null) {
collection[field] = 0;
}
return collection[field] += value;
return (collection[field] += value);
},
store_additions: function(entry, total, by_author) {
if (entry.additions == null) {
entry.additions = 0;
}
this.add(total, "additions", entry.additions);
return this.add(by_author, "additions", entry.additions);
this.add(total, 'additions', entry.additions);
return this.add(by_author, 'additions', entry.additions);
},
store_deletions: function(entry, total, by_author) {
if (entry.deletions == null) {
entry.deletions = 0;
}
this.add(total, "deletions", entry.deletions);
return this.add(by_author, "deletions", entry.deletions);
this.add(total, 'deletions', entry.deletions);
return this.add(by_author, 'deletions', entry.deletions);
},
get_total_data: function(parsed_log, field) {
var log, total_data;
......@@ -95,15 +95,18 @@ export default {
}
log = parsed_log.by_author;
author_data = [];
_.each(log, (function(_this) {
return function(log_entry) {
var parsed_log_entry;
parsed_log_entry = _this.parse_log_entry(log_entry, field, date_range);
if (!_.isEmpty(parsed_log_entry.dates)) {
return author_data.push(parsed_log_entry);
}
};
})(this));
_.each(
log,
(function(_this) {
return function(log_entry) {
var parsed_log_entry;
parsed_log_entry = _this.parse_log_entry(log_entry, field, date_range);
if (!_.isEmpty(parsed_log_entry.dates)) {
return author_data.push(parsed_log_entry);
}
};
})(this),
);
return _.sortBy(author_data, function(d) {
return d[field];
}).reverse();
......@@ -120,16 +123,19 @@ export default {
parsed_entry.additions = 0;
parsed_entry.deletions = 0;
_.each(_.omit(log_entry, 'author_name', 'author_email'), (function(_this) {
return function(value, key) {
if (_this.in_range(value.date, date_range)) {
parsed_entry.dates[value.date] = value[field];
parsed_entry.commits += value.commits;
parsed_entry.additions += value.additions;
return parsed_entry.deletions += value.deletions;
}
};
})(this));
_.each(
_.omit(log_entry, 'author_name', 'author_email'),
(function(_this) {
return function(value, key) {
if (_this.in_range(value.date, date_range)) {
parsed_entry.dates[value.date] = value[field];
parsed_entry.commits += value.commits;
parsed_entry.additions += value.additions;
return (parsed_entry.deletions += value.deletions);
}
};
})(this),
);
return parsed_entry;
},
in_range: function(date, date_range) {
......@@ -139,5 +145,5 @@ export default {
} else {
return false;
}
}
},
};
......@@ -16,7 +16,8 @@ export default () => {
);
const fileBlobPermalinkUrlElement = document.querySelector('.js-data-file-blob-permalink-url');
const fileBlobPermalinkUrl = fileBlobPermalinkUrlElement && fileBlobPermalinkUrlElement.getAttribute('href');
const fileBlobPermalinkUrl =
fileBlobPermalinkUrlElement && fileBlobPermalinkUrlElement.getAttribute('href');
new ShortcutsNavigation(); // eslint-disable-line no-new
......
import ZenMode from '~/zen_mode';
import GLForm from '~/gl_form';
export default function ($formEl) {
export default function($formEl) {
new ZenMode(); // eslint-disable-line no-new
new GLForm($formEl); // eslint-disable-line no-new
}
......@@ -5,7 +5,7 @@ import ZenMode from '~/zen_mode';
import '~/notes/index';
import initIssueableApp from '~/issue_show';
export default function () {
export default function() {
initIssueableApp();
new Issue(); // eslint-disable-line no-new
new ShortcutsIssuable(); // eslint-disable-line no-new
......
<script>
import _ from 'underscore';
import axios from '~/lib/utils/axios_utils';
import createFlash from '~/flash';
import GlModal from '~/vue_shared/components/gl_modal.vue';
import { s__, sprintf } from '~/locale';
import { visitUrl } from '~/lib/utils/url_utility';
import eventHub from '../event_hub';
import _ from 'underscore';
import axios from '~/lib/utils/axios_utils';
import createFlash from '~/flash';
import GlModal from '~/vue_shared/components/gl_modal.vue';
import { s__, sprintf } from '~/locale';
import { visitUrl } from '~/lib/utils/url_utility';
import eventHub from '../event_hub';
export default {
components: {
GlModal,
export default {
components: {
GlModal,
},
props: {
url: {
type: String,
required: true,
},
props: {
url: {
type: String,
required: true,
},
labelTitle: {
type: String,
required: true,
},
labelColor: {
type: String,
required: true,
},
labelTextColor: {
type: String,
required: true,
},
groupName: {
type: String,
required: true,
},
labelTitle: {
type: String,
required: true,
},
computed: {
text() {
return sprintf(s__(`Labels|Promoting %{labelTitle} will make it available for all projects inside %{groupName}.
Existing project labels with the same title will be merged. This action cannot be reversed.`), {
labelColor: {
type: String,
required: true,
},
labelTextColor: {
type: String,
required: true,
},
groupName: {
type: String,
required: true,
},
},
computed: {
text() {
return sprintf(
s__(`Labels|Promoting %{labelTitle} will make it available for all projects inside %{groupName}.
Existing project labels with the same title will be merged. This action cannot be reversed.`),
{
labelTitle: this.labelTitle,
groupName: this.groupName,
});
},
title() {
const label = `<span
},
);
},
title() {
const label = `<span
class="label color-label"
style="background-color: ${this.labelColor}; color: ${this.labelTextColor};"
>${_.escape(this.labelTitle)}</span>`;
return sprintf(s__('Labels|<span>Promote label</span> %{labelTitle} <span>to Group Label?</span>'), {
return sprintf(
s__('Labels|<span>Promote label</span> %{labelTitle} <span>to Group Label?</span>'),
{
labelTitle: label,
}, false);
},
},
false,
);
},
methods: {
onSubmit() {
eventHub.$emit('promoteLabelModal.requestStarted', this.url);
return axios.post(this.url, { params: { format: 'json' } })
.then((response) => {
eventHub.$emit('promoteLabelModal.requestFinished', { labelUrl: this.url, successful: true });
visitUrl(response.data.url);
})
.catch((error) => {
eventHub.$emit('promoteLabelModal.requestFinished', { labelUrl: this.url, successful: false });
createFlash(error);
},
methods: {
onSubmit() {
eventHub.$emit('promoteLabelModal.requestStarted', this.url);
return axios
.post(this.url, { params: { format: 'json' } })
.then(response => {
eventHub.$emit('promoteLabelModal.requestFinished', {
labelUrl: this.url,
successful: true,
});
visitUrl(response.data.url);
})
.catch(error => {
eventHub.$emit('promoteLabelModal.requestFinished', {
labelUrl: this.url,
successful: false,
});
},
createFlash(error);
});
},
};
},
};
</script>
<template>
<gl-modal
......
......@@ -10,20 +10,24 @@ const initLabelIndex = () => {
initLabels();
const onRequestFinished = ({ labelUrl, successful }) => {
const button = document.querySelector(`.js-promote-project-label-button[data-url="${labelUrl}"]`);
const button = document.querySelector(
`.js-promote-project-label-button[data-url="${labelUrl}"]`,
);
if (!successful) {
button.removeAttribute('disabled');
}
};
const onRequestStarted = (labelUrl) => {
const button = document.querySelector(`.js-promote-project-label-button[data-url="${labelUrl}"]`);
const onRequestStarted = labelUrl => {
const button = document.querySelector(
`.js-promote-project-label-button[data-url="${labelUrl}"]`,
);
button.setAttribute('disabled', '');
eventHub.$once('promoteLabelModal.requestFinished', onRequestFinished);
};
const onDeleteButtonClick = (event) => {
const onDeleteButtonClick = event => {
const button = event.currentTarget;
const modalProps = {
labelTitle: button.dataset.labelTitle,
......@@ -37,12 +41,12 @@ const initLabelIndex = () => {
};
const promoteLabelButtons = document.querySelectorAll('.js-promote-project-label-button');
promoteLabelButtons.forEach((button) => {
promoteLabelButtons.forEach(button => {
button.addEventListener('click', onDeleteButtonClick);
});
eventHub.$once('promoteLabelModal.mounted', () => {
promoteLabelButtons.forEach((button) => {
promoteLabelButtons.forEach(button => {
button.removeAttribute('disabled');
});
});
......
......@@ -6,13 +6,15 @@ import BranchGraph from '../../../network/branch_graph';
export default (function() {
function Network(opts) {
var vph;
$("#filter_ref").click(function() {
return $(this).closest('form').submit();
$('#filter_ref').click(function() {
return $(this)
.closest('form')
.submit();
});
this.branch_graph = new BranchGraph($(".network-graph"), opts);
this.branch_graph = new BranchGraph($('.network-graph'), opts);
vph = $(window).height() - 250;
$('.network-graph').css({
'height': vph + 'px'
height: vph + 'px',
});
}
......
import Vue from 'vue';
import PipelineSchedulesCallout from '../shared/components/pipeline_schedules_callout.vue';
document.addEventListener('DOMContentLoaded', () => new Vue({
el: '#pipeline-schedules-callout',
components: {
'pipeline-schedules-callout': PipelineSchedulesCallout,
},
render(createElement) {
return createElement('pipeline-schedules-callout');
},
}));
document.addEventListener(
'DOMContentLoaded',
() =>
new Vue({
el: '#pipeline-schedules-callout',
components: {
'pipeline-schedules-callout': PipelineSchedulesCallout,
},
render(createElement) {
return createElement('pipeline-schedules-callout');
},
}),
);
<script>
import _ from 'underscore';
import _ from 'underscore';
export default {
props: {
initialCronInterval: {
type: String,
required: false,
default: '',
},
},
data() {
return {
inputNameAttribute: 'schedule[cron]',
cronInterval: this.initialCronInterval,
cronIntervalPresets: {
everyDay: '0 4 * * *',
everyWeek: '0 4 * * 0',
everyMonth: '0 4 1 * *',
},
cronSyntaxUrl: 'https://en.wikipedia.org/wiki/Cron',
customInputEnabled: false,
};
export default {
props: {
initialCronInterval: {
type: String,
required: false,
default: '',
},
computed: {
intervalIsPreset() {
return _.contains(this.cronIntervalPresets, this.cronInterval);
},
// The text input is editable when there's a custom interval, or when it's
// a preset interval and the user clicks the 'custom' radio button
isEditable() {
return !!(this.customInputEnabled || !this.intervalIsPreset);
},
data() {
return {
inputNameAttribute: 'schedule[cron]',
cronInterval: this.initialCronInterval,
cronIntervalPresets: {
everyDay: '0 4 * * *',
everyWeek: '0 4 * * 0',
everyMonth: '0 4 1 * *',
},
cronSyntaxUrl: 'https://en.wikipedia.org/wiki/Cron',
customInputEnabled: false,
};
},
computed: {
intervalIsPreset() {
return _.contains(this.cronIntervalPresets, this.cronInterval);
},
watch: {
cronInterval() {
// updates field validation state when model changes, as
// glFieldError only updates on input.
this.$nextTick(() => {
gl.pipelineScheduleFieldErrors.updateFormValidityState();
});
},
// The text input is editable when there's a custom interval, or when it's
// a preset interval and the user clicks the 'custom' radio button
isEditable() {
return !!(this.customInputEnabled || !this.intervalIsPreset);
},
created() {
if (this.intervalIsPreset) {
this.enableCustomInput = false;
}
},
watch: {
cronInterval() {
// updates field validation state when model changes, as
// glFieldError only updates on input.
this.$nextTick(() => {
gl.pipelineScheduleFieldErrors.updateFormValidityState();
});
},
methods: {
toggleCustomInput(shouldEnable) {
this.customInputEnabled = shouldEnable;
},
created() {
if (this.intervalIsPreset) {
this.enableCustomInput = false;
}
},
methods: {
toggleCustomInput(shouldEnable) {
this.customInputEnabled = shouldEnable;
if (shouldEnable) {
// We need to change the value so other radios don't remain selected
// because the model (cronInterval) hasn't changed. The server trims it.
this.cronInterval = `${this.cronInterval} `;
}
},
if (shouldEnable) {
// We need to change the value so other radios don't remain selected
// because the model (cronInterval) hasn't changed. The server trims it.
this.cronInterval = `${this.cronInterval} `;
}
},
};
},
};
</script>
<template>
......
<script>
import Vue from 'vue';
import Cookies from 'js-cookie';
import Translate from '../../../../../vue_shared/translate';
import illustrationSvg from '../icons/intro_illustration.svg';
import Vue from 'vue';
import Cookies from 'js-cookie';
import Translate from '../../../../../vue_shared/translate';
import illustrationSvg from '../icons/intro_illustration.svg';
Vue.use(Translate);
Vue.use(Translate);
const cookieKey = 'pipeline_schedules_callout_dismissed';
const cookieKey = 'pipeline_schedules_callout_dismissed';
export default {
name: 'PipelineSchedulesCallout',
data() {
return {
docsUrl: document.getElementById('pipeline-schedules-callout').dataset.docsUrl,
calloutDismissed: Cookies.get(cookieKey) === 'true',
};
export default {
name: 'PipelineSchedulesCallout',
data() {
return {
docsUrl: document.getElementById('pipeline-schedules-callout').dataset.docsUrl,
calloutDismissed: Cookies.get(cookieKey) === 'true',
};
},
created() {
this.illustrationSvg = illustrationSvg;
},
methods: {
dismissCallout() {
this.calloutDismissed = true;
Cookies.set(cookieKey, this.calloutDismissed, { expires: 365 });
},
created() {
this.illustrationSvg = illustrationSvg;
},
methods: {
dismissCallout() {
this.calloutDismissed = true;
Cookies.set(cookieKey, this.calloutDismissed, { expires: 365 });
},
},
};
},
};
</script>
<template>
<div
......
......@@ -26,8 +26,7 @@ export default class TargetBranchDropdown {
}
formatBranchesList() {
return this.$dropdown.data('data')
.map(val => ({ name: val }));
return this.$dropdown.data('data').map(val => ({ name: val }));
}
setDropdownToggle() {
......
......@@ -11,7 +11,9 @@ Vue.use(Translate);
function initIntervalPatternInput() {
const intervalPatternMount = document.getElementById('interval-pattern-input');
const initialCronInterval = intervalPatternMount ? intervalPatternMount.dataset.initialInterval : '';
const initialCronInterval = intervalPatternMount
? intervalPatternMount.dataset.initialInterval
: '';
return new Vue({
el: intervalPatternMount,
......
......@@ -7,26 +7,29 @@ const options = {
maintainAspectRatio: false,
};
const buildChart = (chartScope) => {
const buildChart = chartScope => {
const data = {
labels: chartScope.labels,
datasets: [{
fillColor: '#707070',
strokeColor: '#707070',
pointColor: '#707070',
pointStrokeColor: '#EEE',
data: chartScope.totalValues,
},
{
fillColor: '#1aaa55',
strokeColor: '#1aaa55',
pointColor: '#1aaa55',
pointStrokeColor: '#fff',
data: chartScope.successValues,
},
datasets: [
{
fillColor: '#707070',
strokeColor: '#707070',
pointColor: '#707070',
pointStrokeColor: '#EEE',
data: chartScope.totalValues,
},
{
fillColor: '#1aaa55',
strokeColor: '#1aaa55',
pointColor: '#1aaa55',
pointStrokeColor: '#fff',
data: chartScope.successValues,
},
],
};
const ctx = $(`#${chartScope.scope}Chart`).get(0).getContext('2d');
const ctx = $(`#${chartScope.scope}Chart`)
.get(0)
.getContext('2d');
new Chart(ctx).Line(data, options);
};
......@@ -36,14 +39,16 @@ document.addEventListener('DOMContentLoaded', () => {
const chartsData = JSON.parse(document.getElementById('pipelinesChartsData').innerHTML);
const data = {
labels: chartTimesData.labels,
datasets: [{
fillColor: 'rgba(220,220,220,0.5)',
strokeColor: 'rgba(220,220,220,1)',
barStrokeWidth: 1,
barValueSpacing: 1,
barDatasetSpacing: 1,
data: chartTimesData.values,
}],
datasets: [
{
fillColor: 'rgba(220,220,220,0.5)',
strokeColor: 'rgba(220,220,220,1)',
barStrokeWidth: 1,
barValueSpacing: 1,
barDatasetSpacing: 1,
data: chartTimesData.values,
},
],
};
if (window.innerWidth < 768) {
......@@ -51,7 +56,11 @@ document.addEventListener('DOMContentLoaded', () => {
options.scaleFontSize = 8;
}
new Chart($('#build_timesChart').get(0).getContext('2d')).Bar(data, options);
new Chart(
$('#build_timesChart')
.get(0)
.getContext('2d'),
).Bar(data, options);
chartsData.forEach(scope => buildChart(scope));
});
......@@ -6,35 +6,39 @@ import { convertPermissionToBoolean } from '../../../../lib/utils/common_utils';
Vue.use(Translate);
document.addEventListener('DOMContentLoaded', () => new Vue({
el: '#pipelines-list-vue',
components: {
pipelinesComponent,
},
data() {
return {
store: new PipelinesStore(),
};
},
created() {
this.dataset = document.querySelector(this.$options.el).dataset;
},
render(createElement) {
return createElement('pipelines-component', {
props: {
store: this.store,
endpoint: this.dataset.endpoint,
helpPagePath: this.dataset.helpPagePath,
emptyStateSvgPath: this.dataset.emptyStateSvgPath,
errorStateSvgPath: this.dataset.errorStateSvgPath,
noPipelinesSvgPath: this.dataset.noPipelinesSvgPath,
autoDevopsPath: this.dataset.helpAutoDevopsPath,
newPipelinePath: this.dataset.newPipelinePath,
canCreatePipeline: convertPermissionToBoolean(this.dataset.canCreatePipeline),
hasGitlabCi: convertPermissionToBoolean(this.dataset.hasGitlabCi),
ciLintPath: this.dataset.ciLintPath,
resetCachePath: this.dataset.resetCachePath,
document.addEventListener(
'DOMContentLoaded',
() =>
new Vue({
el: '#pipelines-list-vue',
components: {
pipelinesComponent,
},
});
},
}));
data() {
return {
store: new PipelinesStore(),
};
},
created() {
this.dataset = document.querySelector(this.$options.el).dataset;
},
render(createElement) {
return createElement('pipelines-component', {
props: {
store: this.store,
endpoint: this.dataset.endpoint,
helpPagePath: this.dataset.helpPagePath,
emptyStateSvgPath: this.dataset.emptyStateSvgPath,
errorStateSvgPath: this.dataset.errorStateSvgPath,
noPipelinesSvgPath: this.dataset.noPipelinesSvgPath,
autoDevopsPath: this.dataset.helpAutoDevopsPath,
newPipelinePath: this.dataset.newPipelinePath,
canCreatePipeline: convertPermissionToBoolean(this.dataset.canCreatePipeline),
hasGitlabCi: convertPermissionToBoolean(this.dataset.hasGitlabCi),
ciLintPath: this.dataset.ciLintPath,
resetCachePath: this.dataset.resetCachePath,
},
});
},
}),
);
......@@ -2,9 +2,12 @@ import Pipelines from '~/pipelines';
export default () => {
const { controllerAction } = document.querySelector('.js-pipeline-container').dataset;
const pipelineStatusUrl = `${document.querySelector('.js-pipeline-tab-link a').getAttribute('href')}/status.json`;
const pipelineStatusUrl = `${document
.querySelector('.js-pipeline-tab-link a')
.getAttribute('href')}/status.json`;
new Pipelines({ // eslint-disable-line no-new
// eslint-disable-next-line no-new
new Pipelines({
initTabs: true,
pipelineStatusUrl,
tabsOptions: {
......
<script>
import projectFeatureToggle from '../../../../../vue_shared/components/toggle_button.vue';
import projectFeatureToggle from '../../../../../vue_shared/components/toggle_button.vue';
export default {
components: {
projectFeatureToggle,
},
export default {
components: {
projectFeatureToggle,
},
model: {
prop: 'value',
event: 'change',
},
model: {
prop: 'value',
event: 'change',
},
props: {
name: {
type: String,
required: false,
default: '',
},
options: {
type: Array,
required: false,
default: () => [],
},
value: {
type: Number,
required: false,
default: 0,
},
disabledInput: {
type: Boolean,
required: false,
default: false,
},
props: {
name: {
type: String,
required: false,
default: '',
},
options: {
type: Array,
required: false,
default: () => [],
},
value: {
type: Number,
required: false,
default: 0,
},
disabledInput: {
type: Boolean,
required: false,
default: false,
},
},
computed: {
featureEnabled() {
return this.value !== 0;
},
computed: {
featureEnabled() {
return this.value !== 0;
},
displayOptions() {
if (this.featureEnabled) {
return this.options;
}
return [
[0, 'Enable feature to choose access level'],
];
},
displayOptions() {
if (this.featureEnabled) {
return this.options;
}
return [[0, 'Enable feature to choose access level']];
},
displaySelectInput() {
return this.disabledInput || !this.featureEnabled || this.displayOptions.length < 2;
},
displaySelectInput() {
return this.disabledInput || !this.featureEnabled || this.displayOptions.length < 2;
},
},
methods: {
toggleFeature(featureEnabled) {
if (featureEnabled === false || this.options.length < 1) {
this.$emit('change', 0);
} else {
const [firstOptionValue] = this.options[this.options.length - 1];
this.$emit('change', firstOptionValue);
}
},
methods: {
toggleFeature(featureEnabled) {
if (featureEnabled === false || this.options.length < 1) {
this.$emit('change', 0);
} else {
const [firstOptionValue] = this.options[this.options.length - 1];
this.$emit('change', firstOptionValue);
}
},
selectOption(e) {
this.$emit('change', Number(e.target.value));
},
selectOption(e) {
this.$emit('change', Number(e.target.value));
},
};
},
};
</script>
<template>
......
<script>
export default {
props: {
label: {
type: String,
required: false,
default: null,
},
helpPath: {
type: String,
required: false,
default: null,
},
helpText: {
type: String,
required: false,
default: null,
},
export default {
props: {
label: {
type: String,
required: false,
default: null,
},
};
helpPath: {
type: String,
required: false,
default: null,
},
helpText: {
type: String,
required: false,
default: null,
},
},
};
</script>
<template>
......
......@@ -5,7 +5,9 @@ export const visibilityOptions = {
};
export const visibilityLevelDescriptions = {
[visibilityOptions.PRIVATE]: 'The project is accessible only by members of the project. Access must be granted explicitly to each user.',
[visibilityOptions.PRIVATE]:
'The project is accessible only by members of the project. Access must be granted explicitly to each user.',
[visibilityOptions.INTERNAL]: 'The project can be accessed by any user who is logged in.',
[visibilityOptions.PUBLIC]: 'The project can be accessed by anyone, regardless of authentication.',
[visibilityOptions.PUBLIC]:
'The project can be accessed by anyone, regardless of authentication.',
};
......@@ -8,8 +8,9 @@ export default function projectAvatar() {
$('.js-project-avatar-input').bind('change', function onClickAvatarInput() {
const form = $(this).closest('form');
// eslint-disable-next-line no-useless-escape
const filename = $(this).val().replace(/^.*[\\\/]/, '');
const filename = $(this)
.val()
.replace(/^.*[\\\/]/, ''); // eslint-disable-line no-useless-escape
return form.find('.js-avatar-filename').text(filename);
});
}
......@@ -21,7 +21,8 @@ document.addEventListener('DOMContentLoaded', () => {
const { deleteWikiUrl, pageTitle } = deleteWikiModalWrapperEl.dataset;
new Vue({ // eslint-disable-line no-new
// eslint-disable-next-line no-new
new Vue({
el: deleteWikiModalWrapperEl,
data: {
deleteWikiUrl: '',
......
......@@ -22,7 +22,7 @@ export default class Search {
fields: ['full_name'],
},
data(term, callback) {
return Api.groups(term, {}, (data) => {
return Api.groups(term, {}, data => {
data.unshift({
full_name: 'Any',
});
......@@ -37,7 +37,7 @@ export default class Search {
return obj.full_name;
},
toggleLabel(obj) {
return `${($groupDropdown.data('defaultLabel'))} ${obj.full_name}`;
return `${$groupDropdown.data('defaultLabel')} ${obj.full_name}`;
},
clicked: () => Search.submitSearch(),
});
......@@ -52,7 +52,7 @@ export default class Search {
},
data: (term, callback) => {
this.getProjectsData(term)
.then((data) => {
.then(data => {
data.unshift({
name_with_namespace: 'Any',
});
......@@ -70,7 +70,7 @@ export default class Search {
return obj.name_with_namespace;
},
toggleLabel(obj) {
return `${($projectDropdown.data('defaultLabel'))} ${obj.name_with_namespace}`;
return `${$projectDropdown.data('defaultLabel')} ${obj.name_with_namespace}`;
},
clicked: () => Search.submitSearch(),
});
......@@ -99,17 +99,24 @@ export default class Search {
}
clearSearchField() {
return $(this.searchInput).val('').trigger('keyup').focus();
return $(this.searchInput)
.val('')
.trigger('keyup')
.focus();
}
getProjectsData(term) {
return new Promise((resolve) => {
return new Promise(resolve => {
if (this.groupId) {
Api.groupProjects(this.groupId, term, {}, resolve);
} else {
Api.projects(term, {
order_by: 'id',
}, resolve);
Api.projects(
term,
{
order_by: 'id',
},
resolve,
);
}
});
}
......
......@@ -20,7 +20,7 @@ export default class SigninTabsMemoizer {
bootstrap() {
const tabs = document.querySelectorAll(this.tabSelector);
if (tabs.length > 0) {
tabs[0].addEventListener('click', (e) => {
tabs[0].addEventListener('click', e => {
if (e.target && e.target.nodeName === 'A') {
const anchorName = e.target.getAttribute('href');
this.saveData(anchorName);
......
......@@ -22,10 +22,10 @@ export default class UsernameValidator {
available: false,
valid: false,
pending: false,
empty: true
empty: true,
};
const debounceTimeout = _.debounce((username) => {
const debounceTimeout = _.debounce(username => {
this.validateUsername(username);
}, debounceTimeoutDuration);
......@@ -81,7 +81,8 @@ export default class UsernameValidator {
this.state.pending = true;
this.state.available = false;
this.renderState();
axios.get(`${gon.relative_url_root}/users/${username}/exists`)
axios
.get(`${gon.relative_url_root}/users/${username}/exists`)
.then(({ data }) => this.setAvailabilityState(data.exists))
.catch(() => flash(__('An error occurred while validating username')));
}
......@@ -100,8 +101,7 @@ export default class UsernameValidator {
clearFieldValidationState() {
this.inputElement.siblings('p').hide();
this.inputElement.removeClass(invalidInputClass)
.removeClass(successInputClass);
this.inputElement.removeClass(invalidInputClass).removeClass(successInputClass);
}
setUnavailableState() {
......
......@@ -13,10 +13,12 @@ function initUserProfile(action) {
new UserTabs({ parentEl: '.user-profile', action });
// hide project limit message
$('.hide-project-limit-message').on('click', (e) => {
$('.hide-project-limit-message').on('click', e => {
e.preventDefault();
Cookies.set('hide_project_limit_message', 'false');
$(this).parents('.project-limit-message').remove();
$(this)
.parents('.project-limit-message')
.remove();
});
}
......
<script>
export default {
props: {
currentRequest: {
type: Object,
required: true,
},
metric: {
type: String,
required: true,
},
export default {
props: {
currentRequest: {
type: Object,
required: true,
},
computed: {
duration() {
return (
this.currentRequest.details[this.metric] &&
this.currentRequest.details[this.metric].duration
);
},
calls() {
return (
this.currentRequest.details[this.metric] && this.currentRequest.details[this.metric].calls
);
},
metric: {
type: String,
required: true,
},
};
},
computed: {
duration() {
return (
this.currentRequest.details[this.metric] &&
this.currentRequest.details[this.metric].duration
);
},
calls() {
return (
this.currentRequest.details[this.metric] && this.currentRequest.details[this.metric].calls
);
},
},
};
</script>
<template>
<div
......
......@@ -9,8 +9,7 @@ export default ({ container }) =>
performanceBarApp: () => import('./components/performance_bar_app.vue'),
},
data() {
const performanceBarData = document.querySelector(this.$options.el)
.dataset;
const performanceBarData = document.querySelector(this.$options.el).dataset;
const store = new PerformanceBarStore();
return {
......
......@@ -11,8 +11,10 @@ export default class PerformanceBarService {
static registerInterceptor(peekUrl, callback) {
const interceptor = response => {
const [fireCallback, requestId, requestUrl] =
PerformanceBarService.callbackParams(response, peekUrl);
const [fireCallback, requestId, requestUrl] = PerformanceBarService.callbackParams(
response,
peekUrl,
);
if (fireCallback) {
callback(requestId, requestUrl);
......@@ -30,10 +32,7 @@ export default class PerformanceBarService {
static removeInterceptor(interceptor) {
axios.interceptors.response.eject(interceptor);
Vue.http.interceptors = _.without(
Vue.http.interceptors,
vueResourceInterceptor,
);
Vue.http.interceptors = _.without(Vue.http.interceptors, vueResourceInterceptor);
}
static callbackParams(response, peekUrl) {
......
......@@ -32,8 +32,6 @@ export default class PerformanceBarStore {
}
canTrackRequest(requestUrl) {
return (
this.requests.filter(request => request.url === requestUrl).length < 2
);
return this.requests.filter(request => request.url === requestUrl).length < 2;
}
}
<script>
import DeprecatedModal from '~/vue_shared/components/deprecated_modal.vue';
import { __, s__, sprintf } from '~/locale';
import csrf from '~/lib/utils/csrf';
import DeprecatedModal from '~/vue_shared/components/deprecated_modal.vue';
import { __, s__, sprintf } from '~/locale';
import csrf from '~/lib/utils/csrf';
export default {
components: {
DeprecatedModal,
export default {
components: {
DeprecatedModal,
},
props: {
actionUrl: {
type: String,
required: true,
},
props: {
actionUrl: {
type: String,
required: true,
},
confirmWithPassword: {
type: Boolean,
required: true,
},
username: {
type: String,
required: true,
},
confirmWithPassword: {
type: Boolean,
required: true,
},
data() {
return {
enteredPassword: '',
enteredUsername: '',
};
username: {
type: String,
required: true,
},
computed: {
csrfToken() {
return csrf.token;
},
inputLabel() {
let confirmationValue;
if (this.confirmWithPassword) {
confirmationValue = __('password');
} else {
confirmationValue = __('username');
}
},
data() {
return {
enteredPassword: '',
enteredUsername: '',
};
},
computed: {
csrfToken() {
return csrf.token;
},
inputLabel() {
let confirmationValue;
if (this.confirmWithPassword) {
confirmationValue = __('password');
} else {
confirmationValue = __('username');
}
confirmationValue = `<code>${confirmationValue}</code>`;
confirmationValue = `<code>${confirmationValue}</code>`;
return sprintf(
s__('Profiles|Type your %{confirmationValue} to confirm:'),
{ confirmationValue },
false,
);
},
text() {
return sprintf(
s__(`Profiles|
return sprintf(
s__('Profiles|Type your %{confirmationValue} to confirm:'),
{ confirmationValue },
false,
);
},
text() {
return sprintf(
s__(`Profiles|
You are about to permanently delete %{yourAccount}, and all of the issues, merge requests, and groups linked to your account.
Once you confirm %{deleteAccount}, it cannot be undone or recovered.`),
{
yourAccount: `<strong>${s__('Profiles|your account')}</strong>`,
deleteAccount: `<strong>${s__('Profiles|Delete Account')}</strong>`,
},
false,
);
},
{
yourAccount: `<strong>${s__('Profiles|your account')}</strong>`,
deleteAccount: `<strong>${s__('Profiles|Delete Account')}</strong>`,
},
false,
);
},
methods: {
canSubmit() {
if (this.confirmWithPassword) {
return this.enteredPassword !== '';
}
},
methods: {
canSubmit() {
if (this.confirmWithPassword) {
return this.enteredPassword !== '';
}
return this.enteredUsername === this.username;
},
onSubmit() {
this.$refs.form.submit();
},
return this.enteredUsername === this.username;
},
onSubmit() {
this.$refs.form.submit();
},
};
},
};
</script>
<template>
......
......@@ -4,20 +4,35 @@ import $ from 'jquery';
import 'cropper';
import _ from 'underscore';
((global) => {
(global => {
// Matches everything but the file name
const FILENAMEREGEX = /^.*[\\\/]/;
class GitLabCrop {
constructor(input, { filename, previewImage, modalCrop, pickImageEl, uploadImageBtn, modalCropImg,
exportWidth = 200, exportHeight = 200, cropBoxWidth = 200, cropBoxHeight = 200 } = {}) {
constructor(
input,
{
filename,
previewImage,
modalCrop,
pickImageEl,
uploadImageBtn,
modalCropImg,
exportWidth = 200,
exportHeight = 200,
cropBoxWidth = 200,
cropBoxHeight = 200,
} = {},
) {
this.onUploadImageBtnClick = this.onUploadImageBtnClick.bind(this);
this.onModalHide = this.onModalHide.bind(this);
this.onModalShow = this.onModalShow.bind(this);
this.onPickImageClick = this.onPickImageClick.bind(this);
this.fileInput = $(input);
this.modalCropImg = _.isString(this.modalCropImg) ? $(this.modalCropImg) : this.modalCropImg;
this.fileInput.attr('name', `${this.fileInput.attr('name')}-trigger`).attr('id', `${this.fileInput.attr('id')}-trigger`);
this.fileInput
.attr('name', `${this.fileInput.attr('name')}-trigger`)
.attr('id', `${this.fileInput.attr('id')}-trigger`);
this.exportWidth = exportWidth;
this.exportHeight = exportHeight;
this.cropBoxWidth = cropBoxWidth;
......@@ -59,7 +74,7 @@ import _ from 'underscore';
btn = this;
return _this.onActionBtnClick(btn);
});
return this.croppedImageBlob = null;
return (this.croppedImageBlob = null);
}
onPickImageClick() {
......@@ -94,9 +109,9 @@ import _ from 'underscore';
width: cropBoxWidth,
height: cropBoxHeight,
left: (container.width - cropBoxWidth) / 2,
top: (container.height - cropBoxHeight) / 2
top: (container.height - cropBoxHeight) / 2,
});
}
},
});
}
......@@ -116,7 +131,7 @@ import _ from 'underscore';
var data, result;
data = $(btn).data();
if (this.modalCropImg.data('cropper') && data.method) {
return result = this.modalCropImg.cropper(data.method, data.option);
return (result = this.modalCropImg.cropper(data.method, data.option));
}
}
......@@ -127,7 +142,7 @@ import _ from 'underscore';
readFile(input) {
var _this, reader;
_this = this;
reader = new FileReader;
reader = new FileReader();
reader.onload = () => {
_this.modalCropImg.attr('src', reader.result);
return _this.modalCrop.modal('show');
......@@ -145,7 +160,7 @@ import _ from 'underscore';
array.push(binary.charCodeAt(i));
}
return new Blob([new Uint8Array(array)], {
type: 'image/png'
type: 'image/png',
});
}
......@@ -157,11 +172,13 @@ import _ from 'underscore';
}
setBlob() {
this.dataURL = this.modalCropImg.cropper('getCroppedCanvas', {
width: 200,
height: 200
}).toDataURL('image/png');
return this.croppedImageBlob = this.dataURLtoBlob(this.dataURL);
this.dataURL = this.modalCropImg
.cropper('getCroppedCanvas', {
width: 200,
height: 200,
})
.toDataURL('image/png');
return (this.croppedImageBlob = this.dataURLtoBlob(this.dataURL));
}
getBlob() {
......
......@@ -26,11 +26,7 @@ export default class Profile {
}
bindEvents() {
$('.js-preferences-form').on(
'change.preference',
'input[type=radio]',
this.submitForm,
);
$('.js-preferences-form').on('change.preference', 'input[type=radio]', this.submitForm);
$('#user_notification_email').on('change', this.submitForm);
$('#user_notified_of_own_activity').on('change', this.submitForm);
this.form.on('submit', this.onSubmitForm);
......
......@@ -46,8 +46,12 @@ export default class ProtectedBranchCreate {
onSelect() {
// Enable submit button
const $branchInput = this.$form.find('input[name="protected_branch[name]"]');
const $allowedToMergeInput = this.$form.find('input[name="protected_branch[merge_access_levels_attributes][0][access_level]"]');
const $allowedToPushInput = this.$form.find('input[name="protected_branch[push_access_levels_attributes][0][access_level]"]');
const $allowedToMergeInput = this.$form.find(
'input[name="protected_branch[merge_access_levels_attributes][0][access_level]"]',
);
const $allowedToPushInput = this.$form.find(
'input[name="protected_branch[push_access_levels_attributes][0][access_level]"]',
);
const completedForm = !(
$branchInput.val() &&
$allowedToMergeInput.length &&
......
......@@ -29,8 +29,12 @@ export default class ProtectedBranchEdit {
}
onSelect() {
const $allowedToMergeInput = this.$wrap.find(`input[name="${this.$allowedToMergeDropdown.data('fieldName')}"]`);
const $allowedToPushInput = this.$wrap.find(`input[name="${this.$allowedToPushDropdown.data('fieldName')}"]`);
const $allowedToMergeInput = this.$wrap.find(
`input[name="${this.$allowedToMergeDropdown.data('fieldName')}"]`,
);
const $allowedToPushInput = this.$wrap.find(
`input[name="${this.$allowedToPushDropdown.data('fieldName')}"]`,
);
// Do not update if one dropdown has not selected any option
if (!($allowedToMergeInput.length && $allowedToPushInput.length)) return;
......@@ -38,25 +42,36 @@ export default class ProtectedBranchEdit {
this.$allowedToMergeDropdown.disable();
this.$allowedToPushDropdown.disable();
axios.patch(this.$wrap.data('url'), {
protected_branch: {
merge_access_levels_attributes: [{
id: this.$allowedToMergeDropdown.data('accessLevelId'),
access_level: $allowedToMergeInput.val(),
}],
push_access_levels_attributes: [{
id: this.$allowedToPushDropdown.data('accessLevelId'),
access_level: $allowedToPushInput.val(),
}],
},
}).then(() => {
this.$allowedToMergeDropdown.enable();
this.$allowedToPushDropdown.enable();
}).catch(() => {
this.$allowedToMergeDropdown.enable();
this.$allowedToPushDropdown.enable();
flash('Failed to update branch!', 'alert', document.querySelector('.js-protected-branches-list'));
});
axios
.patch(this.$wrap.data('url'), {
protected_branch: {
merge_access_levels_attributes: [
{
id: this.$allowedToMergeDropdown.data('accessLevelId'),
access_level: $allowedToMergeInput.val(),
},
],
push_access_levels_attributes: [
{
id: this.$allowedToPushDropdown.data('accessLevelId'),
access_level: $allowedToPushInput.val(),
},
],
},
})
.then(() => {
this.$allowedToMergeDropdown.enable();
this.$allowedToPushDropdown.enable();
})
.catch(() => {
this.$allowedToMergeDropdown.enable();
this.$allowedToPushDropdown.enable();
flash(
'Failed to update branch!',
'alert',
document.querySelector('.js-protected-branches-list'),
);
});
}
}
......@@ -40,7 +40,9 @@ export default class ProtectedTagCreate {
const $tagInput = this.$form.find('input[name="protected_tag[name]"]');
const $allowedToCreateInput = this.$form.find('#create_access_levels_attributes');
this.$form.find('input[type="submit"]').prop('disabled', !($tagInput.val() && $allowedToCreateInput.length));
this.$form
.find('input[type="submit"]')
.prop('disabled', !($tagInput.val() && $allowedToCreateInput.length));
}
static getProtectedTags(term, callback) {
......
......@@ -21,26 +21,33 @@ export default class ProtectedTagEdit {
}
onSelect() {
const $allowedToCreateInput = this.$wrap.find(`input[name="${this.$allowedToCreateDropdownButton.data('fieldName')}"]`);
const $allowedToCreateInput = this.$wrap.find(
`input[name="${this.$allowedToCreateDropdownButton.data('fieldName')}"]`,
);
// Do not update if one dropdown has not selected any option
if (!$allowedToCreateInput.length) return;
this.$allowedToCreateDropdownButton.disable();
axios.patch(this.$wrap.data('url'), {
protected_tag: {
create_access_levels_attributes: [{
id: this.$allowedToCreateDropdownButton.data('accessLevelId'),
access_level: $allowedToCreateInput.val(),
}],
},
}).then(() => {
this.$allowedToCreateDropdownButton.enable();
}).catch(() => {
this.$allowedToCreateDropdownButton.enable();
flash('Failed to update tag!', 'alert', document.querySelector('.js-protected-tags-list'));
});
axios
.patch(this.$wrap.data('url'), {
protected_tag: {
create_access_levels_attributes: [
{
id: this.$allowedToCreateDropdownButton.data('accessLevelId'),
access_level: $allowedToCreateInput.val(),
},
],
},
})
.then(() => {
this.$allowedToCreateDropdownButton.enable();
})
.catch(() => {
this.$allowedToCreateDropdownButton.enable();
flash('Failed to update tag!', 'alert', document.querySelector('.js-protected-tags-list'));
});
}
}
<script>
import { mapGetters, mapActions } from 'vuex';
import Flash from '../../flash';
import store from '../stores';
import collapsibleContainer from './collapsible_container.vue';
import { errorMessages, errorMessagesTypes } from '../constants';
import { mapGetters, mapActions } from 'vuex';
import Flash from '../../flash';
import store from '../stores';
import collapsibleContainer from './collapsible_container.vue';
import { errorMessages, errorMessagesTypes } from '../constants';
export default {
name: 'RegistryListApp',
components: {
collapsibleContainer,
export default {
name: 'RegistryListApp',
components: {
collapsibleContainer,
},
props: {
endpoint: {
type: String,
required: true,
},
props: {
endpoint: {
type: String,
required: true,
},
},
store,
computed: {
...mapGetters([
'isLoading',
'repos',
]),
},
created() {
this.setMainEndpoint(this.endpoint);
},
mounted() {
this.fetchRepos()
.catch(() => Flash(errorMessages[errorMessagesTypes.FETCH_REPOS]));
},
methods: {
...mapActions([
'setMainEndpoint',
'fetchRepos',
]),
},
};
},
store,
computed: {
...mapGetters(['isLoading', 'repos']),
},
created() {
this.setMainEndpoint(this.endpoint);
},
mounted() {
this.fetchRepos().catch(() => Flash(errorMessages[errorMessagesTypes.FETCH_REPOS]));
},
methods: {
...mapActions(['setMainEndpoint', 'fetchRepos']),
},
};
</script>
<template>
<div>
......
<script>
import { mapActions } from 'vuex';
import Flash from '../../flash';
import clipboardButton from '../../vue_shared/components/clipboard_button.vue';
import tooltip from '../../vue_shared/directives/tooltip';
import tableRegistry from './table_registry.vue';
import { errorMessages, errorMessagesTypes } from '../constants';
import { __ } from '../../locale';
import { mapActions } from 'vuex';
import Flash from '../../flash';
import clipboardButton from '../../vue_shared/components/clipboard_button.vue';
import tooltip from '../../vue_shared/directives/tooltip';
import tableRegistry from './table_registry.vue';
import { errorMessages, errorMessagesTypes } from '../constants';
import { __ } from '../../locale';
export default {
name: 'CollapsibeContainerRegisty',
components: {
clipboardButton,
tableRegistry,
export default {
name: 'CollapsibeContainerRegisty',
components: {
clipboardButton,
tableRegistry,
},
directives: {
tooltip,
},
props: {
repo: {
type: Object,
required: true,
},
directives: {
tooltip,
},
props: {
repo: {
type: Object,
required: true,
},
},
data() {
return {
isOpen: false,
};
},
methods: {
...mapActions([
'fetchRepos',
'fetchList',
'deleteRepo',
]),
},
data() {
return {
isOpen: false,
};
},
methods: {
...mapActions(['fetchRepos', 'fetchList', 'deleteRepo']),
toggleRepo() {
this.isOpen = !this.isOpen;
toggleRepo() {
this.isOpen = !this.isOpen;
if (this.isOpen) {
this.fetchList({ repo: this.repo })
.catch(() => this.showError(errorMessagesTypes.FETCH_REGISTRY));
}
},
if (this.isOpen) {
this.fetchList({ repo: this.repo }).catch(() =>
this.showError(errorMessagesTypes.FETCH_REGISTRY),
);
}
},
handleDeleteRepository() {
this.deleteRepo(this.repo)
.then(() => {
Flash(__('This container registry has been scheduled for deletion.'), 'notice');
this.fetchRepos();
})
.catch(() => this.showError(errorMessagesTypes.DELETE_REPO));
},
handleDeleteRepository() {
this.deleteRepo(this.repo)
.then(() => {
Flash(__('This container registry has been scheduled for deletion.'), 'notice');
this.fetchRepos();
})
.catch(() => this.showError(errorMessagesTypes.DELETE_REPO));
},
showError(message) {
Flash(errorMessages[message]);
},
showError(message) {
Flash(errorMessages[message]);
},
};
},
};
</script>
<template>
......
<script>
import { mapActions } from 'vuex';
import { n__ } from '../../locale';
import Flash from '../../flash';
import clipboardButton from '../../vue_shared/components/clipboard_button.vue';
import tablePagination from '../../vue_shared/components/table_pagination.vue';
import tooltip from '../../vue_shared/directives/tooltip';
import timeagoMixin from '../../vue_shared/mixins/timeago';
import { errorMessages, errorMessagesTypes } from '../constants';
import { numberToHumanSize } from '../../lib/utils/number_utils';
import { mapActions } from 'vuex';
import { n__ } from '../../locale';
import Flash from '../../flash';
import clipboardButton from '../../vue_shared/components/clipboard_button.vue';
import tablePagination from '../../vue_shared/components/table_pagination.vue';
import tooltip from '../../vue_shared/directives/tooltip';
import timeagoMixin from '../../vue_shared/mixins/timeago';
import { errorMessages, errorMessagesTypes } from '../constants';
import { numberToHumanSize } from '../../lib/utils/number_utils';
export default {
components: {
clipboardButton,
tablePagination,
export default {
components: {
clipboardButton,
tablePagination,
},
directives: {
tooltip,
},
mixins: [timeagoMixin],
props: {
repo: {
type: Object,
required: true,
},
directives: {
tooltip,
},
computed: {
shouldRenderPagination() {
return this.repo.pagination.total > this.repo.pagination.perPage;
},
mixins: [
timeagoMixin,
],
props: {
repo: {
type: Object,
required: true,
},
},
computed: {
shouldRenderPagination() {
return this.repo.pagination.total > this.repo.pagination.perPage;
},
},
methods: {
...mapActions([
'fetchList',
'deleteRegistry',
]),
},
methods: {
...mapActions(['fetchList', 'deleteRegistry']),
layers(item) {
return item.layers ? n__('%d layer', '%d layers', item.layers) : '';
},
layers(item) {
return item.layers ? n__('%d layer', '%d layers', item.layers) : '';
},
formatSize(size) {
return numberToHumanSize(size);
},
formatSize(size) {
return numberToHumanSize(size);
},
handleDeleteRegistry(registry) {
this.deleteRegistry(registry)
.then(() => this.fetchList({ repo: this.repo }))
.catch(() => this.showError(errorMessagesTypes.DELETE_REGISTRY));
},
handleDeleteRegistry(registry) {
this.deleteRegistry(registry)
.then(() => this.fetchList({ repo: this.repo }))
.catch(() => this.showError(errorMessagesTypes.DELETE_REGISTRY));
},
onPageChange(pageNumber) {
this.fetchList({ repo: this.repo, page: pageNumber })
.catch(() => this.showError(errorMessagesTypes.FETCH_REGISTRY));
},
onPageChange(pageNumber) {
this.fetchList({ repo: this.repo, page: pageNumber }).catch(() =>
this.showError(errorMessagesTypes.FETCH_REGISTRY),
);
},
showError(message) {
Flash(errorMessages[message]);
},
showError(message) {
Flash(errorMessages[message]);
},
};
},
};
</script>
<template>
<div>
......
......@@ -4,22 +4,23 @@ import Translate from '../vue_shared/translate';
Vue.use(Translate);
export default () => new Vue({
el: '#js-vue-registry-images',
components: {
registryApp,
},
data() {
const { dataset } = document.querySelector(this.$options.el);
return {
endpoint: dataset.endpoint,
};
},
render(createElement) {
return createElement('registry-app', {
props: {
endpoint: this.endpoint,
},
});
},
});
export default () =>
new Vue({
el: '#js-vue-registry-images',
components: {
registryApp,
},
data() {
const { dataset } = document.querySelector(this.$options.el);
return {
endpoint: dataset.endpoint,
};
},
render(createElement) {
return createElement('registry-app', {
props: {
endpoint: this.endpoint,
},
});
},
});
......@@ -2,7 +2,6 @@ import * as types from './mutation_types';
import { parseIntPagination, normalizeHeaders } from '../../lib/utils/common_utils';
export default {
[types.SET_MAIN_ENDPOINT](state, endpoint) {
Object.assign(state, { endpoint });
},
......
<script>
import { mapActions, mapGetters, mapState } from 'vuex';
import { s__ } from '~/locale';
import { componentNames } from './issue_body';
import ReportSection from './report_section.vue';
import SummaryRow from './summary_row.vue';
import IssuesList from './issues_list.vue';
import Modal from './modal.vue';
import createStore from '../store';
import { summaryTextBuilder, reportTextBuilder, statusIcon } from '../store/utils';
import { mapActions, mapGetters, mapState } from 'vuex';
import { s__ } from '~/locale';
import { componentNames } from './issue_body';
import ReportSection from './report_section.vue';
import SummaryRow from './summary_row.vue';
import IssuesList from './issues_list.vue';
import Modal from './modal.vue';
import createStore from '../store';
import { summaryTextBuilder, reportTextBuilder, statusIcon } from '../store/utils';
export default {
name: 'GroupedTestReportsApp',
store: createStore(),
components: {
ReportSection,
SummaryRow,
IssuesList,
Modal,
export default {
name: 'GroupedTestReportsApp',
store: createStore(),
components: {
ReportSection,
SummaryRow,
IssuesList,
Modal,
},
props: {
endpoint: {
type: String,
required: true,
},
props: {
endpoint: {
type: String,
required: true,
},
},
componentNames,
computed: {
...mapState([
'reports',
'isLoading',
'hasError',
'summary',
]),
...mapState({
modalTitle: state => state.modal.title || '',
modalData: state => state.modal.data || {},
}),
...mapGetters([
'summaryStatus',
]),
groupedSummaryText() {
if (this.isLoading) {
return s__('Reports|Test summary results are being parsed');
}
},
componentNames,
computed: {
...mapState(['reports', 'isLoading', 'hasError', 'summary']),
...mapState({
modalTitle: state => state.modal.title || '',
modalData: state => state.modal.data || {},
}),
...mapGetters(['summaryStatus']),
groupedSummaryText() {
if (this.isLoading) {
return s__('Reports|Test summary results are being parsed');
}
if (this.hasError) {
return s__('Reports|Test summary failed loading results');
}
if (this.hasError) {
return s__('Reports|Test summary failed loading results');
}
return summaryTextBuilder(s__('Reports|Test summary'), this.summary);
},
return summaryTextBuilder(s__('Reports|Test summary'), this.summary);
},
created() {
this.setEndpoint(this.endpoint);
},
created() {
this.setEndpoint(this.endpoint);
this.fetchReports();
this.fetchReports();
},
methods: {
...mapActions(['setEndpoint', 'fetchReports']),
reportText(report) {
const summary = report.summary || {};
return reportTextBuilder(report.name, summary);
},
getReportIcon(report) {
return statusIcon(report.status);
},
methods: {
...mapActions(['setEndpoint', 'fetchReports']),
reportText(report) {
const summary = report.summary || {};
return reportTextBuilder(report.name, summary);
},
getReportIcon(report) {
return statusIcon(report.status);
},
shouldRenderIssuesList(report) {
return (
report.existing_failures.length > 0 ||
report.new_failures.length > 0 ||
report.resolved_failures.length > 0
);
},
shouldRenderIssuesList(report) {
return (
report.existing_failures.length > 0 ||
report.new_failures.length > 0 ||
report.resolved_failures.length > 0
);
},
};
},
};
</script>
<template>
<report-section
......
<script>
import Icon from '~/vue_shared/components/icon.vue';
import {
STATUS_FAILED,
STATUS_NEUTRAL,
STATUS_SUCCESS,
} from '../constants';
import { STATUS_FAILED, STATUS_NEUTRAL, STATUS_SUCCESS } from '../constants';
export default {
name: 'IssueStatusIcon',
......
<script>
import IssuesBlock from '~/reports/components/report_issues.vue';
import {
STATUS_SUCCESS,
STATUS_FAILED,
STATUS_NEUTRAL,
} from '~/reports/constants';
import { STATUS_SUCCESS, STATUS_FAILED, STATUS_NEUTRAL } from '~/reports/constants';
/**
* Renders block of issues
......
<script>
import Modal from '~/vue_shared/components/gl_modal.vue';
import LoadingButton from '~/vue_shared/components/loading_button.vue';
import CodeBlock from '~/vue_shared/components/code_block.vue';
import { fieldTypes } from '../constants';
import Modal from '~/vue_shared/components/gl_modal.vue';
import LoadingButton from '~/vue_shared/components/loading_button.vue';
import CodeBlock from '~/vue_shared/components/code_block.vue';
import { fieldTypes } from '../constants';
export default {
components: {
Modal,
LoadingButton,
CodeBlock,
export default {
components: {
Modal,
LoadingButton,
CodeBlock,
},
props: {
title: {
type: String,
required: true,
},
props: {
title: {
type: String,
required: true,
},
modalData: {
type: Object,
required: true,
},
modalData: {
type: Object,
required: true,
},
fieldTypes,
};
},
fieldTypes,
};
</script>
<template>
<modal
......
<script>
import { mapActions } from 'vuex';
import { mapActions } from 'vuex';
export default {
name: 'TestIssueBody',
props: {
issue: {
type: Object,
required: true,
},
// failed || success
status: {
type: String,
required: true,
},
isNew: {
type: Boolean,
required: false,
default: false,
},
export default {
name: 'TestIssueBody',
props: {
issue: {
type: Object,
required: true,
},
methods: {
...mapActions(['openModal']),
// failed || success
status: {
type: String,
required: true,
},
};
isNew: {
type: Boolean,
required: false,
default: false,
},
},
methods: {
...mapActions(['openModal']),
},
};
</script>
<template>
<div class="report-block-list-issue-description prepend-top-5 append-bottom-5">
......
......@@ -43,9 +43,11 @@ export const fetchReports = ({ state, dispatch }) => {
},
data: state.endpoint,
method: 'getReports',
successCallback: ({ data, status }) => dispatch('receiveReportsSuccess', {
data, status,
}),
successCallback: ({ data, status }) =>
dispatch('receiveReportsSuccess', {
data,
status,
}),
errorCallback: () => dispatch('receiveReportsError'),
});
......
......@@ -7,9 +7,10 @@ import state from './state';
Vue.use(Vuex);
export default () => new Vuex.Store({
actions,
mutations,
getters,
state: state(),
});
export default () =>
new Vuex.Store({
actions,
mutations,
getters,
state: state(),
});
......@@ -4,4 +4,3 @@ export const REQUEST_REPORTS = 'REQUEST_REPORTS';
export const RECEIVE_REPORTS_SUCCESS = 'RECEIVE_REPORTS_SUCCESS';
export const RECEIVE_REPORTS_ERROR = 'RECEIVE_REPORTS_ERROR';
export const SET_ISSUE_MODAL_DATA = 'SET_ISSUE_MODAL_DATA';
......@@ -19,7 +19,6 @@ export default {
state.status = response.status;
state.reports = response.suites;
},
[types.RECEIVE_REPORTS_ERROR](state) {
state.isLoading = false;
......@@ -36,7 +35,7 @@ export default {
[types.SET_ISSUE_MODAL_DATA](state, payload) {
state.modal.title = payload.issue.name;
Object.keys(payload.issue).forEach((key) => {
Object.keys(payload.issue).forEach(key => {
if (Object.prototype.hasOwnProperty.call(state.modal.data, key)) {
state.modal.data[key] = {
...state.modal.data[key],
......
......@@ -57,5 +57,4 @@ export default () => ({
},
},
},
});
......@@ -69,7 +69,8 @@ export default {
this.loading = false;
}
this.mediator.saveAssignees(this.field)
this.mediator
.saveAssignees(this.field)
.then(setLoadingFalse.bind(this))
.catch(() => {
setLoadingFalse();
......
......@@ -56,11 +56,7 @@ export default {
.update('issue', { confidential })
.then(() => window.location.reload())
.catch(() => {
Flash(
__(
'Something went wrong trying to change the confidentiality of this issue',
),
);
Flash(__('Something went wrong trying to change the confidentiality of this issue'));
});
},
},
......
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment