Commit 06b6daac authored by DJ Mountney's avatar DJ Mountney

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

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