diff options
Diffstat (limited to 'app/assets/javascripts/pages')
29 files changed, 366 insertions, 319 deletions
diff --git a/app/assets/javascripts/pages/admin/application_settings/general/components/signup_form.vue b/app/assets/javascripts/pages/admin/application_settings/general/components/signup_form.vue index 5ecacb84d65..ccb449f96e1 100644 --- a/app/assets/javascripts/pages/admin/application_settings/general/components/signup_form.vue +++ b/app/assets/javascripts/pages/admin/application_settings/general/components/signup_form.vue @@ -12,6 +12,7 @@ import { import { toSafeInteger } from 'lodash'; import csrf from '~/lib/utils/csrf'; import { __, n__, s__, sprintf } from '~/locale'; +import glFeatureFlagMixin from '~/vue_shared/mixins/gl_feature_flags_mixin'; import SignupCheckbox from './signup_checkbox.vue'; const DENYLIST_TYPE_RAW = 'raw'; @@ -31,7 +32,12 @@ export default { GlLink, SignupCheckbox, GlModal, + PasswordComplexityCheckboxGroup: () => + import( + 'ee_component/pages/admin/application_settings/general/components/password_complexity_checkbox_group.vue' + ), }, + mixins: [glFeatureFlagMixin()], inject: [ 'host', 'settingsPath', @@ -178,6 +184,9 @@ export default { this.submitForm(); }, + setPasswordComplexity({ name, value }) { + this.$set(this.form, name, value); + }, submitForm() { this.$refs.form.submit(); }, @@ -291,9 +300,7 @@ export default { <template #description> <gl-sprintf :message=" - s__( - 'ApplicationSettings|See GitLab\'s %{linkStart}Password Policy Guidelines%{linkEnd}.', - ) + s__('ApplicationSettings|See %{linkStart}password policy guidelines%{linkEnd}.') " > <template #link="{ content }"> @@ -305,6 +312,10 @@ export default { </template> </gl-form-group> + <password-complexity-checkbox-group + v-if="glFeatures.passwordComplexity" + @set-password-complexity="setPasswordComplexity" + /> <gl-form-group :description="$options.i18n.domainAllowListDescription" :label="$options.i18n.domainAllowListLabel" diff --git a/app/assets/javascripts/pages/admin/application_settings/signup_restrictions.js b/app/assets/javascripts/pages/admin/application_settings/signup_restrictions.js index a50d8de0e88..0d5c55cb87b 100644 --- a/app/assets/javascripts/pages/admin/application_settings/signup_restrictions.js +++ b/app/assets/javascripts/pages/admin/application_settings/signup_restrictions.js @@ -18,6 +18,10 @@ export default function initSignupRestrictions(elementSelector = '#js-signup-for 'domainDenylistEnabled', 'denylistTypeRawSelected', 'emailRestrictionsEnabled', + 'passwordNumberRequired', + 'passwordLowercaseRequired', + 'passwordUppercaseRequired', + 'passwordSymbolRequired', ], }); diff --git a/app/assets/javascripts/pages/dashboard/todos/index/todos.js b/app/assets/javascripts/pages/dashboard/todos/index/todos.js index 44299d235d5..e45a40bd44c 100644 --- a/app/assets/javascripts/pages/dashboard/todos/index/todos.js +++ b/app/assets/javascripts/pages/dashboard/todos/index/todos.js @@ -28,20 +28,32 @@ export default class Todos { } unbindEvents() { - $('.js-done-todo, .js-undo-todo, .js-add-todo').off('click', this.updateRowStateClickedWrapper); - $('.js-todos-mark-all', '.js-todos-undo-all').off('click', this.updateallStateClickedWrapper); - $('.todo').off('click', this.goToTodoUrl); - $('.todo').off('auxclick', this.goToTodoUrl); + document.querySelectorAll('.js-done-todo, .js-undo-todo, .js-add-todo').forEach((el) => { + el.removeEventListener('click', this.updateRowStateClickedWrapper); + }); + document.querySelectorAll('.js-todos-mark-all, .js-todos-undo-all').forEach((el) => { + el.removeEventListener('click', this.updateallStateClickedWrapper); + }); + document.querySelectorAll('.todo').forEach((el) => { + el.removeEventListener('click', this.goToTodoUrl); + el.removeEventListener('auxclick', this.goToTodoUrl); + }); } bindEvents() { this.updateRowStateClickedWrapper = this.updateRowStateClicked.bind(this); this.updateAllStateClickedWrapper = this.updateAllStateClicked.bind(this); - $('.js-done-todo, .js-undo-todo, .js-add-todo').on('click', this.updateRowStateClickedWrapper); - $('.js-todos-mark-all, .js-todos-undo-all').on('click', this.updateAllStateClickedWrapper); - $('.todo').on('click', this.goToTodoUrl); - $('.todo').on('auxclick', this.goToTodoUrl); + document.querySelectorAll('.js-done-todo, .js-undo-todo, .js-add-todo').forEach((el) => { + el.addEventListener('click', this.updateRowStateClickedWrapper); + }); + document.querySelectorAll('.js-todos-mark-all, .js-todos-undo-all').forEach((el) => { + el.addEventListener('click', this.updateAllStateClickedWrapper); + }); + document.querySelectorAll('.todo').forEach((el) => { + el.addEventListener('click', this.goToTodoUrl); + el.addEventListener('auxclick', this.goToTodoUrl); + }); } initFilters() { @@ -181,7 +193,13 @@ export default class Todos { } updateBadges(data) { - $(document).trigger('todo:toggle', data.count); + const event = new CustomEvent('todo:toggle', { + detail: { + count: data.count, + }, + }); + + document.dispatchEvent(event); document.querySelector('.js-todos-pending .js-todos-badge').innerHTML = addDelimiter( data.count, ); diff --git a/app/assets/javascripts/pages/groups/group_members/index.js b/app/assets/javascripts/pages/groups/group_members/index.js index c7c2f6f773e..62d47cb49b8 100644 --- a/app/assets/javascripts/pages/groups/group_members/index.js +++ b/app/assets/javascripts/pages/groups/group_members/index.js @@ -5,12 +5,11 @@ import initInviteMembersModal from '~/invite_members/init_invite_members_modal'; import initInviteMembersTrigger from '~/invite_members/init_invite_members_trigger'; import { s__ } from '~/locale'; import { initMembersApp } from '~/members'; -import { MEMBER_TYPES } from '~/members/constants'; +import { MEMBER_TYPES, EE_APP_OPTIONS } from 'ee_else_ce/members/constants'; import { groupLinkRequestFormatter } from '~/members/utils'; const SHARED_FIELDS = ['account', 'maxRole', 'expiration', 'actions']; - -initMembersApp(document.querySelector('.js-group-members-list-app'), { +const APP_OPTIONS = { [MEMBER_TYPES.user]: { tableFields: SHARED_FIELDS.concat(['source', 'granted', 'userCreatedAt', 'lastActivityOn']), tableAttrs: { tr: { 'data-qa-selector': 'member_row' } }, @@ -61,7 +60,10 @@ initMembersApp(document.querySelector('.js-group-members-list-app'), { tableFields: SHARED_FIELDS.concat('requested'), requestFormatter: groupMemberRequestFormatter, }, -}); + ...EE_APP_OPTIONS, +}; + +initMembersApp(document.querySelector('.js-group-members-list-app'), APP_OPTIONS); initInviteMembersModal(); initInviteGroupsModal(); diff --git a/app/assets/javascripts/pages/groups/runners/index.js b/app/assets/javascripts/pages/groups/runners/index/index.js index ca1a6bdab75..ca1a6bdab75 100644 --- a/app/assets/javascripts/pages/groups/runners/index.js +++ b/app/assets/javascripts/pages/groups/runners/index/index.js diff --git a/app/assets/javascripts/pages/groups/runners/show/index.js b/app/assets/javascripts/pages/groups/runners/show/index.js new file mode 100644 index 00000000000..c59e3b80dc1 --- /dev/null +++ b/app/assets/javascripts/pages/groups/runners/show/index.js @@ -0,0 +1,3 @@ +import { initGroupRunnerShow } from '~/runner/group_runner_show'; + +initGroupRunnerShow(); diff --git a/app/assets/javascripts/pages/projects/branches/new/index.js b/app/assets/javascripts/pages/projects/branches/new/index.js index 364223f1898..dbae89b5ade 100644 --- a/app/assets/javascripts/pages/projects/branches/new/index.js +++ b/app/assets/javascripts/pages/projects/branches/new/index.js @@ -1,8 +1,7 @@ -import $ from 'jquery'; import NewBranchForm from '~/new_branch_form'; // eslint-disable-next-line no-new new NewBranchForm( - $('.js-create-branch-form'), + document.querySelector('.js-create-branch-form'), JSON.parse(document.getElementById('availableRefs').innerHTML), ); diff --git a/app/assets/javascripts/pages/projects/forks/new/components/fork_form.vue b/app/assets/javascripts/pages/projects/forks/new/components/fork_form.vue index 701bf0c1e1d..f92a40e057f 100644 --- a/app/assets/javascripts/pages/projects/forks/new/components/fork_form.vue +++ b/app/assets/javascripts/pages/projects/forks/new/components/fork_form.vue @@ -329,7 +329,7 @@ export default { </div> <p class="gl-mt-n5 gl-text-gray-500"> - {{ s__('ForkProject|Want to house several dependent projects under the same namespace?') }} + {{ s__('ForkProject|Want to organize several dependent projects under the same namespace?') }} <gl-link :href="newGroupPath" target="_blank"> {{ s__('ForkProject|Create a group') }} </gl-link> diff --git a/app/assets/javascripts/pages/projects/google_cloud/configuration/index.js b/app/assets/javascripts/pages/projects/google_cloud/configuration/index.js new file mode 100644 index 00000000000..abececa44ee --- /dev/null +++ b/app/assets/javascripts/pages/projects/google_cloud/configuration/index.js @@ -0,0 +1,3 @@ +import init from '~/google_cloud/configuration/index'; + +init(); diff --git a/app/assets/javascripts/pages/projects/google_cloud/databases/index.js b/app/assets/javascripts/pages/projects/google_cloud/databases/index.js new file mode 100644 index 00000000000..5482324f1cd --- /dev/null +++ b/app/assets/javascripts/pages/projects/google_cloud/databases/index.js @@ -0,0 +1,3 @@ +import init from '~/google_cloud/databases/index'; + +init(); diff --git a/app/assets/javascripts/pages/projects/google_cloud/deployments/index.js b/app/assets/javascripts/pages/projects/google_cloud/deployments/index.js new file mode 100644 index 00000000000..b5a29b3825b --- /dev/null +++ b/app/assets/javascripts/pages/projects/google_cloud/deployments/index.js @@ -0,0 +1,3 @@ +import init from '~/google_cloud/deployments/index'; + +init(); diff --git a/app/assets/javascripts/pages/projects/google_cloud/gcp_regions/index.js b/app/assets/javascripts/pages/projects/google_cloud/gcp_regions/index.js new file mode 100644 index 00000000000..fb66e2fa051 --- /dev/null +++ b/app/assets/javascripts/pages/projects/google_cloud/gcp_regions/index.js @@ -0,0 +1,3 @@ +import init from '~/google_cloud/gcp_regions/index'; + +init(); diff --git a/app/assets/javascripts/pages/projects/google_cloud/index.js b/app/assets/javascripts/pages/projects/google_cloud/index.js deleted file mode 100644 index 4506ea8efd1..00000000000 --- a/app/assets/javascripts/pages/projects/google_cloud/index.js +++ /dev/null @@ -1,3 +0,0 @@ -import initGoogleCloud from '~/google_cloud/index'; - -initGoogleCloud(); diff --git a/app/assets/javascripts/pages/projects/google_cloud/service_accounts/index.js b/app/assets/javascripts/pages/projects/google_cloud/service_accounts/index.js new file mode 100644 index 00000000000..8b644c2b324 --- /dev/null +++ b/app/assets/javascripts/pages/projects/google_cloud/service_accounts/index.js @@ -0,0 +1,3 @@ +import init from '~/google_cloud/service_accounts/index'; + +init(); diff --git a/app/assets/javascripts/pages/projects/jobs/index/index.js b/app/assets/javascripts/pages/projects/jobs/index/index.js index 75194499a7f..eb3a24f38a8 100644 --- a/app/assets/javascripts/pages/projects/jobs/index/index.js +++ b/app/assets/javascripts/pages/projects/jobs/index/index.js @@ -1,23 +1,3 @@ -import Vue from 'vue'; import initJobsTable from '~/jobs/components/table'; -import GlCountdown from '~/vue_shared/components/gl_countdown.vue'; -if (gon.features?.jobsTableVue) { - initJobsTable(); -} else { - const remainingTimeElements = document.querySelectorAll('.js-remaining-time'); - - remainingTimeElements.forEach( - (el) => - new Vue({ - el, - render(h) { - return h(GlCountdown, { - props: { - endDateString: el.dateTime, - }, - }); - }, - }), - ); -} +initJobsTable(); diff --git a/app/assets/javascripts/pages/projects/learn_gitlab/components/included_in_trial_indicator.vue b/app/assets/javascripts/pages/projects/learn_gitlab/components/included_in_trial_indicator.vue new file mode 100644 index 00000000000..693dc6a15ad --- /dev/null +++ b/app/assets/javascripts/pages/projects/learn_gitlab/components/included_in_trial_indicator.vue @@ -0,0 +1,15 @@ +<script> +import { s__ } from '~/locale'; + +export default { + name: 'IncludedInTrialIndicator', + i18n: { + trialOnly: s__('LearnGitlab|- Included in trial'), + }, +}; +</script> +<template> + <span class="gl-font-style-italic gl-text-gray-500" data-testid="trial-only"> + {{ $options.i18n.trialOnly }} + </span> +</template> diff --git a/app/assets/javascripts/pages/projects/learn_gitlab/components/learn_gitlab.vue b/app/assets/javascripts/pages/projects/learn_gitlab/components/learn_gitlab.vue index db9ef4df8af..54e15b6552c 100644 --- a/app/assets/javascripts/pages/projects/learn_gitlab/components/learn_gitlab.vue +++ b/app/assets/javascripts/pages/projects/learn_gitlab/components/learn_gitlab.vue @@ -38,14 +38,16 @@ export default { actionsData: this.actions, }; }, - maxValue: Object.keys(ACTION_LABELS).length, actionSections: Object.keys(ACTION_SECTIONS), computed: { + maxValue() { + return Object.keys(this.actionsData).length; + }, progressValue() { return Object.values(this.actionsData).filter((a) => a.completed).length; }, progressPercentage() { - return Math.round((this.progressValue / this.$options.maxValue) * 100); + return Math.round((this.progressValue / this.maxValue) * 100); }, }, mounted() { @@ -125,7 +127,7 @@ export default { <template #percentSymbol>%</template> </gl-sprintf> </p> - <gl-progress-bar :value="progressValue" :max="$options.maxValue" /> + <gl-progress-bar :value="progressValue" :max="maxValue" /> </div> <div class="row"> <div diff --git a/app/assets/javascripts/pages/projects/learn_gitlab/components/learn_gitlab_info_card.vue b/app/assets/javascripts/pages/projects/learn_gitlab/components/learn_gitlab_info_card.vue deleted file mode 100644 index 09cc0032871..00000000000 --- a/app/assets/javascripts/pages/projects/learn_gitlab/components/learn_gitlab_info_card.vue +++ /dev/null @@ -1,70 +0,0 @@ -<script> -import { GlLink, GlCard, GlIcon } from '@gitlab/ui'; -import { s__ } from '~/locale'; - -export default { - name: 'LearnGitlabInfoCard', - components: { GlLink, GlCard, GlIcon }, - i18n: { - trial: s__('Learn GitLab|Trial only'), - }, - props: { - title: { - required: true, - type: String, - }, - description: { - required: true, - type: String, - }, - actionLabel: { - required: true, - type: String, - }, - url: { - required: true, - type: String, - }, - completed: { - required: true, - type: Boolean, - }, - svg: { - required: true, - type: String, - }, - trialRequired: { - default: false, - required: false, - type: Boolean, - }, - }, -}; -</script> -<template> - <gl-card class="gl-pt-0"> - <div class="gl-text-right gl-h-5"> - <gl-icon - v-if="completed" - name="check-circle-filled" - class="gl-text-green-500" - :size="16" - data-testid="completed-icon" - /> - <span - v-else-if="trialRequired" - class="gl-text-gray-500 gl-font-sm gl-font-style-italic" - data-testid="trial-only" - >{{ $options.i18n.trial }}</span - > - </div> - <div - class="gl-text-center gl-display-flex gl-justify-content-center gl-align-items-center gl-flex-direction-column learn-gitlab-info-card-content" - > - <img :src="svg" :alt="actionLabel" /> - <h6>{{ title }}</h6> - <p class="gl-font-sm gl-text-gray-700">{{ description }}</p> - <gl-link :href="url" target="_blank" rel="noopener noreferrer" /> - </div> - </gl-card> -</template> diff --git a/app/assets/javascripts/pages/projects/learn_gitlab/components/learn_gitlab_section_link.vue b/app/assets/javascripts/pages/projects/learn_gitlab/components/learn_gitlab_section_link.vue index 1912477758b..4eab0cccb06 100644 --- a/app/assets/javascripts/pages/projects/learn_gitlab/components/learn_gitlab_section_link.vue +++ b/app/assets/javascripts/pages/projects/learn_gitlab/components/learn_gitlab_section_link.vue @@ -6,6 +6,7 @@ import { isExperimentVariant } from '~/experimentation/utils'; import eventHub from '~/invite_members/event_hub'; import { s__, __ } from '~/locale'; import { ACTION_LABELS } from '../constants'; +import IncludedInTrialIndicator from './included_in_trial_indicator.vue'; export default { name: 'LearnGitlabSectionLink', @@ -15,12 +16,12 @@ export default { GlButton, GlPopover, GitlabExperiment, + IncludedInTrialIndicator, }, directives: { GlTooltip, }, i18n: { - trialOnly: s__('LearnGitlab|Trial only'), contactAdmin: s__('LearnGitlab|Contact your administrator to start a free Ultimate trial.'), viewAdminList: s__('LearnGitlab|View administrator list'), watchHow: __('Watch how'), @@ -41,12 +42,6 @@ export default { }; }, computed: { - linkTitle() { - return ACTION_LABELS[this.action].title; - }, - trialOnly() { - return ACTION_LABELS[this.action].trialRequired; - }, showInviteModalLink() { return ( this.action === 'userAdded' && isExperimentVariant('invite_for_help_continuous_onboarding') @@ -55,49 +50,51 @@ export default { openInNewTab() { return ACTION_LABELS[this.action]?.openInNewTab === true || this.value.openInNewTab === true; }, - linkToVideoTutorial() { - return ACTION_LABELS[this.action].videoTutorial; - }, }, methods: { openModal() { eventHub.$emit('openModal', { source: 'learn_gitlab' }); }, + actionLabelValue(value) { + return ACTION_LABELS[this.action][value]; + }, }, }; </script> <template> <div class="gl-mb-4"> - <div v-if="trialOnly" class="gl-font-style-italic gl-text-gray-500" data-testid="trial-only"> - {{ $options.i18n.trialOnly }} - </div> <div class="flex align-items-center"> <span v-if="value.completed" class="gl-text-green-500"> <gl-icon name="check-circle-filled" :size="16" data-testid="completed-icon" /> - {{ linkTitle }} + {{ actionLabelValue('title') }} + <included-in-trial-indicator v-if="actionLabelValue('trialRequired')" /> </span> - <gl-link - v-else-if="showInviteModalLink" - data-track-action="click_link" - :data-track-label="linkTitle" - data-track-property="Growth::Activation::Experiment::InviteForHelpContinuousOnboarding" - data-testid="invite-for-help-continuous-onboarding-experiment-link" - @click="openModal" - > - {{ linkTitle }} - </gl-link> - <gl-link - v-else-if="value.enabled" - :target="openInNewTab ? '_blank' : '_self'" - :href="value.url" - data-testid="uncompleted-learn-gitlab-link" - data-track-action="click_link" - :data-track-label="linkTitle" - > - {{ linkTitle }} - </gl-link> + <div v-else-if="showInviteModalLink"> + <gl-link + data-track-action="click_link" + :data-track-label="actionLabelValue('trackLabel')" + data-track-property="Growth::Activation::Experiment::InviteForHelpContinuousOnboarding" + data-testid="invite-for-help-continuous-onboarding-experiment-link" + @click="openModal" + >{{ actionLabelValue('title') }}</gl-link + > + + <included-in-trial-indicator v-if="actionLabelValue('trialRequired')" /> + </div> + <div v-else-if="value.enabled"> + <gl-link + :target="openInNewTab ? '_blank' : '_self'" + :href="value.url" + data-testid="uncompleted-learn-gitlab-link" + data-track-action="click_link" + :data-track-label="actionLabelValue('trackLabel')" + >{{ actionLabelValue('title') }}</gl-link + > + + <included-in-trial-indicator v-if="actionLabelValue('trialRequired')" /> + </div> <template v-else> - <div data-testid="disabled-learn-gitlab-link">{{ linkTitle }}</div> + <div data-testid="disabled-learn-gitlab-link">{{ actionLabelValue('title') }}</div> <gl-button :id="popoverId" category="tertiary" @@ -127,19 +124,19 @@ export default { <template #control></template> <template #candidate> <gl-button - v-if="linkToVideoTutorial" + v-if="actionLabelValue('videoTutorial')" v-gl-tooltip category="tertiary" icon="live-preview" :title="$options.i18n.watchHow" :aria-label="$options.i18n.watchHow" - :href="linkToVideoTutorial" + :href="actionLabelValue('videoTutorial')" target="_blank" class="ml-auto" size="small" data-testid="video-tutorial-link" data-track-action="click_video_link" - :data-track-label="linkTitle" + :data-track-label="actionLabelValue('trackLabel')" data-track-property="Growth::Conversion::Experiment::LearnGitLab" data-track-experiment="video_tutorials_continuous_onboarding" /> diff --git a/app/assets/javascripts/pages/projects/learn_gitlab/constants/index.js b/app/assets/javascripts/pages/projects/learn_gitlab/constants/index.js index 05bacd9b350..cb1a0302d91 100644 --- a/app/assets/javascripts/pages/projects/learn_gitlab/constants/index.js +++ b/app/assets/javascripts/pages/projects/learn_gitlab/constants/index.js @@ -2,9 +2,10 @@ import { s__ } from '~/locale'; export const ACTION_LABELS = { gitWrite: { - title: s__('LearnGitLab|Create or import a repository'), - actionLabel: s__('LearnGitLab|Create or import a repository'), + title: s__('LearnGitLab|Create a repository'), + actionLabel: s__('LearnGitLab|Create a repository'), description: s__('LearnGitLab|Create or import your first repository into your new project.'), + trackLabel: 'create_a_repository', section: 'workspace', position: 1, }, @@ -14,20 +15,23 @@ export const ACTION_LABELS = { description: s__( 'LearnGitLab|GitLab works best as a team. Invite your colleague to enjoy all features.', ), + trackLabel: 'invite_your_colleagues', section: 'workspace', position: 0, }, pipelineCreated: { - title: s__('LearnGitLab|Set up CI/CD'), - actionLabel: s__('LearnGitLab|Set-up CI/CD'), + title: s__("LearnGitLab|Set up your first project's CI/CD"), + actionLabel: s__('LearnGitLab|Set up CI/CD'), description: s__('LearnGitLab|Save time by automating your integration and deployment tasks.'), + trackLabel: 'set_up_your_first_project_s_ci_cd', section: 'workspace', position: 2, }, trialStarted: { - title: s__('LearnGitLab|Start a free Ultimate trial'), + title: s__('LearnGitLab|Start a free trial of GitLab Ultimate'), actionLabel: s__('LearnGitLab|Try GitLab Ultimate for free'), description: s__('LearnGitLab|Try all GitLab features for 30 days, no credit card required.'), + trackLabel: 'start_a_free_trial_of_gitlab_ultimate', section: 'workspace', position: 3, openInNewTab: true, @@ -38,6 +42,7 @@ export const ACTION_LABELS = { description: s__( 'LearnGitLab|Prevent unexpected changes to important assets by assigning ownership of files and paths.', ), + trackLabel: 'add_code_owners', trialRequired: true, section: 'workspace', position: 4, @@ -45,9 +50,10 @@ export const ACTION_LABELS = { videoTutorial: 'https://vimeo.com/670896787', }, requiredMrApprovalsEnabled: { - title: s__('LearnGitLab|Add merge request approval'), + title: s__('LearnGitLab|Enable require merge approvals'), actionLabel: s__('LearnGitLab|Enable require merge approvals'), description: s__('LearnGitLab|Route code reviews to the right reviewers, every time.'), + trackLabel: 'enable_require_merge_approvals', trialRequired: true, section: 'workspace', position: 5, @@ -55,28 +61,52 @@ export const ACTION_LABELS = { videoTutorial: 'https://vimeo.com/670904904', }, mergeRequestCreated: { - title: s__('LearnGitLab|Submit a merge request'), + title: s__('LearnGitLab|Submit a merge request (MR)'), actionLabel: s__('LearnGitLab|Submit a merge request (MR)'), description: s__('LearnGitLab|Review and edit proposed changes to source code.'), + trackLabel: 'submit_a_merge_request_mr', section: 'plan', position: 1, }, - securityScanEnabled: { - title: s__('LearnGitLab|Run a Security scan using CI/CD'), - actionLabel: s__('LearnGitLab|Run a Security scan using CI/CD'), - description: s__('LearnGitLab|Scan your code to uncover vulnerabilities before deploying.'), - section: 'deploy', - position: 1, - }, issueCreated: { title: s__('LearnGitLab|Create an issue'), actionLabel: s__('LearnGitLab|Create an issue'), description: s__( 'LearnGitLab|Create/import issues (tickets) to collaborate on ideas and plan work.', ), + trackLabel: 'create_an_issue', section: 'plan', position: 0, }, + securityScanEnabled: { + title: s__('LearnGitLab|Run a Security scan using CI/CD'), + actionLabel: s__('LearnGitLab|Run a Security scan using CI/CD'), + description: s__('LearnGitLab|Scan your code to uncover vulnerabilities before deploying.'), + trackLabel: 'run_a_security_scan_using_ci_cd', + section: 'deploy', + position: 1, + }, + licenseScanningRun: { + title: s__('LearnGitLab|Scan dependencies for licenses'), + trackLabel: 'scan_dependencies_for_licenses', + trialRequired: true, + section: 'deploy', + position: 2, + }, + secureDependencyScanningRun: { + title: s__('LearnGitLab|Scan dependencies for vulnerabilities'), + trackLabel: 'scan_dependencies_for_vulnerabilities', + trialRequired: true, + section: 'deploy', + position: 3, + }, + secureDastRun: { + title: s__('LearnGitLab|Analyze your application for vulnerabilities with DAST'), + trackLabel: 'analyze_your_application_for_vulnerabilities_with_dast', + trialRequired: true, + section: 'deploy', + position: 4, + }, }; export const ACTION_SECTIONS = { diff --git a/app/assets/javascripts/pages/projects/logs/index.js b/app/assets/javascripts/pages/projects/logs/index.js deleted file mode 100644 index 0cff1ffc27e..00000000000 --- a/app/assets/javascripts/pages/projects/logs/index.js +++ /dev/null @@ -1,3 +0,0 @@ -import logsBundle from '~/logs'; - -logsBundle(); diff --git a/app/assets/javascripts/pages/projects/merge_requests/init_merge_request_show.js b/app/assets/javascripts/pages/projects/merge_requests/init_merge_request_show.js index 48e360ce762..2db804e1ad8 100644 --- a/app/assets/javascripts/pages/projects/merge_requests/init_merge_request_show.js +++ b/app/assets/javascripts/pages/projects/merge_requests/init_merge_request_show.js @@ -9,6 +9,7 @@ import initSourcegraph from '~/sourcegraph'; import ZenMode from '~/zen_mode'; import initAwardsApp from '~/emoji/awards_app'; import MrWidgetHowToMergeModal from '~/vue_merge_request_widget/components/mr_widget_how_to_merge_modal.vue'; +import { initMrExperienceSurvey } from '~/surveys/merge_request_experience'; import getStateQuery from './queries/get_state.query.graphql'; export default function initMergeRequestShow() { @@ -18,6 +19,7 @@ export default function initMergeRequestShow() { initSourcegraph(); initIssuableSidebar(); initAwardsApp(document.getElementById('js-vue-awards-block')); + initMrExperienceSurvey(); const el = document.querySelector('.js-mr-status-box'); const { iid, issuableType, projectPath } = el.dataset; diff --git a/app/assets/javascripts/pages/projects/project_members/index.js b/app/assets/javascripts/pages/projects/project_members/index.js index bf4fb5f3b7e..9a7fd74fd8c 100644 --- a/app/assets/javascripts/pages/projects/project_members/index.js +++ b/app/assets/javascripts/pages/projects/project_members/index.js @@ -1,4 +1,5 @@ -import initImportAProjectModal from '~/invite_members/init_import_a_project_modal'; +import initImportProjectMembersTrigger from '~/invite_members/init_import_project_members_trigger'; +import initImportProjectMembersModal from '~/invite_members/init_import_project_members_modal'; import initInviteGroupTrigger from '~/invite_members/init_invite_group_trigger'; import initInviteMembersModal from '~/invite_members/init_invite_members_modal'; import initInviteGroupsModal from '~/invite_members/init_invite_groups_modal'; @@ -9,11 +10,12 @@ import { MEMBER_TYPES } from '~/members/constants'; import { groupLinkRequestFormatter } from '~/members/utils'; import { projectMemberRequestFormatter } from '~/projects/members/utils'; -initImportAProjectModal(); +initImportProjectMembersModal(); initInviteMembersModal(); initInviteGroupsModal(); initInviteMembersTrigger(); initInviteGroupTrigger(); +initImportProjectMembersTrigger(); const SHARED_FIELDS = ['account', 'maxRole', 'expiration', 'actions']; initMembersApp(document.querySelector('.js-project-members-list-app'), { @@ -38,7 +40,7 @@ initMembersApp(document.querySelector('.js-project-members-list-app'), { }, }, [MEMBER_TYPES.group]: { - tableFields: SHARED_FIELDS.concat('granted'), + tableFields: SHARED_FIELDS.concat(['source', 'granted']), tableAttrs: { table: { 'data-qa-selector': 'groups_list' }, tr: { 'data-qa-selector': 'group_row' }, @@ -46,7 +48,7 @@ initMembersApp(document.querySelector('.js-project-members-list-app'), { requestFormatter: groupLinkRequestFormatter, filteredSearchBar: { show: true, - tokens: [], + tokens: ['groups_with_inherited_permissions'], searchParam: 'search_groups', placeholder: s__('Members|Search groups'), recentSearchesStorageKey: 'project_group_links', diff --git a/app/assets/javascripts/pages/projects/settings/ci_cd/show/index.js b/app/assets/javascripts/pages/projects/settings/ci_cd/show/index.js index 43ab829f5f9..6a9bd34db22 100644 --- a/app/assets/javascripts/pages/projects/settings/ci_cd/show/index.js +++ b/app/assets/javascripts/pages/projects/settings/ci_cd/show/index.js @@ -9,6 +9,7 @@ import { initInstallRunner } from '~/pages/shared/mount_runner_instructions'; import initSharedRunnersToggle from '~/projects/settings/mount_shared_runners_toggle'; import initSettingsPanels from '~/settings_panels'; import { initTokenAccess } from '~/token_access'; +import { initCiSecureFiles } from '~/ci_secure_files'; // Initialize expandable settings panels initSettingsPanels(); @@ -41,3 +42,4 @@ initSharedRunnersToggle(); initInstallRunner(); initRunnerAwsDeployments(); initTokenAccess(); +initCiSecureFiles(); diff --git a/app/assets/javascripts/pages/projects/shared/permissions/components/settings_panel.vue b/app/assets/javascripts/pages/projects/shared/permissions/components/settings_panel.vue index 81b0dbec0bd..f2c30870a68 100644 --- a/app/assets/javascripts/pages/projects/shared/permissions/components/settings_panel.vue +++ b/app/assets/javascripts/pages/projects/shared/permissions/components/settings_panel.vue @@ -61,6 +61,10 @@ export default { GlFormCheckbox, GlToggle, ConfirmDanger, + otherProjectSettings: () => + import( + 'jh_component/pages/projects/shared/permissions/components/other_project_settings.vue' + ), }, mixins: [settingsMixin, glFeatureFlagsMixin()], @@ -182,6 +186,10 @@ export default { required: false, default: false, }, + membersPagePath: { + type: String, + required: true, + }, }, data() { const defaults = { @@ -521,12 +529,22 @@ export default { /> </div> </div> - <span v-if="!visibilityAllowed(visibilityLevel)" class="form-text text-muted">{{ - s__( - 'ProjectSettings|Visibility options for this fork are limited by the current visibility of the source project.', - ) - }}</span> - <span class="form-text text-muted">{{ visibilityLevelDescription }}</span> + <span + v-if="!visibilityAllowed(visibilityLevel)" + class="gl-display-block gl-text-gray-500 gl-mt-2" + >{{ + s__( + 'ProjectSettings|Visibility options for this fork are limited by the current visibility of the source project.', + ) + }}</span + > + <span class="gl-display-block gl-text-gray-500 gl-mt-2"> + <gl-sprintf :message="visibilityLevelDescription"> + <template #membersPageLink="{ content }"> + <gl-link class="gl-link" :href="membersPagePath">{{ content }}</gl-link> + </template> + </gl-sprintf> + </span> <div v-if="showAdditonalSettings" class="gl-mt-4"> <strong class="gl-display-block">{{ s__('ProjectSettings|Additional options') }}</strong> <label @@ -891,6 +909,7 @@ export default { <template #help>{{ $options.i18n.pucWarningHelpText }}</template> </gl-form-checkbox> </project-setting-row> + <other-project-settings /> <confirm-danger v-if="isVisibilityReduced" button-variant="confirm" diff --git a/app/assets/javascripts/pages/projects/shared/permissions/constants.js b/app/assets/javascripts/pages/projects/shared/permissions/constants.js index fb1acd5311c..cfca9d400e3 100644 --- a/app/assets/javascripts/pages/projects/shared/permissions/constants.js +++ b/app/assets/javascripts/pages/projects/shared/permissions/constants.js @@ -8,12 +8,10 @@ export const visibilityOptions = { export const visibilityLevelDescriptions = { [visibilityOptions.PRIVATE]: __( - 'The project is accessible only by members of the project. Access must be granted explicitly to each user.', - ), - [visibilityOptions.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.', + `Only accessible by %{membersPageLinkStart}project members%{membersPageLinkEnd}. Membership must be explicitly granted to each user.`, ), + [visibilityOptions.INTERNAL]: __('Accessible by any user who is logged in.'), + [visibilityOptions.PUBLIC]: __('Accessible by anyone, regardless of authentication.'), }; export const featureAccessLevel = { diff --git a/app/assets/javascripts/pages/projects/work_items/index.js b/app/assets/javascripts/pages/projects/work_items/index.js index 11c257611f0..6eef2352e2c 100644 --- a/app/assets/javascripts/pages/projects/work_items/index.js +++ b/app/assets/javascripts/pages/projects/work_items/index.js @@ -1,3 +1,5 @@ import { initWorkItemsRoot } from '~/work_items/index'; +import initInviteMembersModal from '~/invite_members/init_invite_members_modal'; initWorkItemsRoot(); +initInviteMembersModal(); diff --git a/app/assets/javascripts/pages/shared/wikis/components/wiki_content.vue b/app/assets/javascripts/pages/shared/wikis/components/wiki_content.vue index 7c23f60954a..e92f386a29e 100644 --- a/app/assets/javascripts/pages/shared/wikis/components/wiki_content.vue +++ b/app/assets/javascripts/pages/shared/wikis/components/wiki_content.vue @@ -3,6 +3,7 @@ import { GlSkeletonLoader, GlSafeHtmlDirective, GlAlert } from '@gitlab/ui'; import createFlash from '~/flash'; import { __ } from '~/locale'; import axios from '~/lib/utils/axios_utils'; +import { handleLocationHash } from '~/lib/utils/common_utils'; import { renderGFM } from '../render_gfm_facade'; export default { @@ -43,6 +44,7 @@ export default { this.$nextTick() .then(() => { renderGFM(this.$refs.content); + handleLocationHash(); }) .catch(() => createFlash({ diff --git a/app/assets/javascripts/pages/shared/wikis/components/wiki_form.vue b/app/assets/javascripts/pages/shared/wikis/components/wiki_form.vue index 024b3bc9595..3c22844434d 100644 --- a/app/assets/javascripts/pages/shared/wikis/components/wiki_form.vue +++ b/app/assets/javascripts/pages/shared/wikis/components/wiki_form.vue @@ -1,5 +1,16 @@ <script> -import { GlForm, GlIcon, GlLink, GlButton, GlSprintf, GlAlert } from '@gitlab/ui'; +import { + GlForm, + GlIcon, + GlLink, + GlButton, + GlSprintf, + GlAlert, + GlFormGroup, + GlFormInput, + GlFormSelect, +} from '@gitlab/ui'; +import LocalStorageSync from '~/vue_shared/components/local_storage_sync.vue'; import axios from '~/lib/utils/axios_utils'; import csrf from '~/lib/utils/csrf'; import { setUrlFragment } from '~/lib/utils/url_utility'; @@ -75,12 +86,16 @@ export default { }, components: { GlAlert, + GlIcon, GlForm, + GlFormGroup, + GlFormInput, + GlFormSelect, GlSprintf, - GlIcon, GlLink, GlButton, MarkdownField, + LocalStorageSync, ContentEditor: () => import( /* webpackChunkName: 'content_editor' */ '~/content_editor/components/content_editor.vue' @@ -186,6 +201,10 @@ export default { this.useContentEditor = !this.useContentEditor; }, + setUseContentEditor(value) { + this.useContentEditor = value; + }, + async handleFormSubmit(e) { e.preventDefault(); @@ -305,150 +324,151 @@ export default { name="wiki[last_commit_sha]" :value="pageInfo.lastCommitSha" /> - <div class="form-group row"> - <div class="col-sm-2 col-form-label"> - <label class="control-label-full-width" for="wiki_title">{{ - $options.i18n.title.label - }}</label> - </div> - <div class="col-sm-10"> - <input - id="wiki_title" - v-model="title" - name="wiki[title]" - type="text" - class="form-control" - data-qa-selector="wiki_title_textbox" - :required="true" - :autofocus="!pageInfo.persisted" - :placeholder="$options.i18n.title.placeholder" - @input="updateCommitMessage" - /> - <span class="gl-display-inline-block gl-max-w-full gl-mt-2 gl-text-gray-600"> - <gl-icon class="gl-mr-n1" name="bulb" /> - {{ titleHelpText }} - <gl-link :href="helpPath" target="_blank"> - {{ $options.i18n.title.helpText.learnMore }} - </gl-link> - </span> - </div> - </div> - <div class="form-group row"> - <div class="col-sm-2 col-form-label"> - <label class="control-label-full-width" for="wiki_format">{{ - $options.i18n.format.label - }}</label> + + <div class="row"> + <div class="col-sm-9"> + <gl-form-group :label="$options.i18n.title.label" label-for="wiki_title"> + <template #description> + <gl-icon class="gl-mr-n1" name="bulb" /> + {{ titleHelpText }} + <gl-link :href="helpPath" target="_blank"> + {{ $options.i18n.title.helpText.learnMore }} + </gl-link> + </template> + + <gl-form-input + id="wiki_title" + v-model="title" + name="wiki[title]" + type="text" + class="form-control" + data-qa-selector="wiki_title_textbox" + :required="true" + :autofocus="!pageInfo.persisted" + :placeholder="$options.i18n.title.placeholder" + @input="updateCommitMessage" + /> + </gl-form-group> </div> - <div class="col-sm-10"> - <select - id="wiki_format" - v-model="format" - class="form-control" - name="wiki[format]" - :disabled="isContentEditorActive" - > - <option v-for="(key, label) of formatOptions" :key="key" :value="key"> - {{ label }} - </option> - </select> + + <div class="col-sm-3 row-sm-10"> + <gl-form-group :label="$options.i18n.format.label" label-for="wiki_format"> + <gl-form-select + id="wiki_format" + v-model="format" + name="wiki[format]" + :disabled="isContentEditorActive" + class="form-control" + :value="formatOptions.Markdown" + > + <option v-for="(key, label) of formatOptions" :key="key" :value="key"> + {{ label }} + </option> + </gl-form-select> + </gl-form-group> </div> </div> - <div class="form-group row" data-testid="wiki-form-content-fieldset"> - <div class="col-sm-2 col-form-label"> - <label class="control-label-full-width" for="wiki_content">{{ - $options.i18n.content.label - }}</label> - </div> - <div class="col-sm-10"> - <div v-if="isMarkdownFormat" class="gl-display-flex gl-justify-content-end gl-mb-3"> - <gl-button - data-testid="toggle-editing-mode-button" - data-qa-selector="editing_mode_button" - :data-qa-mode="toggleEditingModeButtonText" - variant="link" - @click="toggleEditingMode" - >{{ toggleEditingModeButtonText }}</gl-button - > - </div> - <markdown-field - v-if="!isContentEditorActive" - :markdown-preview-path="pageInfo.markdownPreviewPath" - :can-attach-file="true" - :enable-autocomplete="true" - :textarea-value="content" - :markdown-docs-path="pageInfo.markdownHelpPath" - :uploads-path="pageInfo.uploadsPath" - :enable-preview="isMarkdownFormat" - class="bordered-box" - > - <template #textarea> - <textarea - id="wiki_content" - ref="textarea" - v-model="content" - name="wiki[content]" - class="note-textarea js-gfm-input js-autosize markdown-area" - dir="auto" - data-supports-quick-actions="false" - data-qa-selector="wiki_content_textarea" - :autofocus="pageInfo.persisted" - :aria-label="$options.i18n.content.label" - :placeholder="$options.i18n.content.placeholder" - @input="handleContentChange" + + <div class="row" data-testid="wiki-form-content-fieldset"> + <div class="col-sm-12 row-sm-5"> + <gl-form-group> + <div v-if="isMarkdownFormat" class="gl-display-flex gl-justify-content-end gl-mb-3"> + <gl-button + data-testid="toggle-editing-mode-button" + data-qa-selector="editing_mode_button" + :data-qa-mode="toggleEditingModeButtonText" + variant="link" + @click="toggleEditingMode" + >{{ toggleEditingModeButtonText }}</gl-button > - </textarea> - </template> - </markdown-field> - <div v-if="isContentEditorActive"> - <content-editor - :render-markdown="renderMarkdown" - :uploads-path="pageInfo.uploadsPath" - @initialized="loadInitialContent" - @change="handleContentEditorChange" + </div> + <local-storage-sync + storage-key="gl-wiki-content-editor-enabled" + :value="useContentEditor" + @input="setUseContentEditor" /> - <input id="wiki_content" v-model.trim="content" type="hidden" name="wiki[content]" /> - </div> + <markdown-field + v-if="!isContentEditorActive" + :markdown-preview-path="pageInfo.markdownPreviewPath" + :can-attach-file="true" + :enable-autocomplete="true" + :textarea-value="content" + :markdown-docs-path="pageInfo.markdownHelpPath" + :uploads-path="pageInfo.uploadsPath" + :enable-preview="isMarkdownFormat" + class="bordered-box" + > + <template #textarea> + <textarea + id="wiki_content" + ref="textarea" + v-model="content" + name="wiki[content]" + class="note-textarea js-gfm-input js-autosize markdown-area" + dir="auto" + data-supports-quick-actions="false" + data-qa-selector="wiki_content_textarea" + :autofocus="pageInfo.persisted" + :aria-label="$options.i18n.content.label" + :placeholder="$options.i18n.content.placeholder" + @input="handleContentChange" + > + </textarea> + </template> + </markdown-field> + <div v-if="isContentEditorActive"> + <content-editor + :render-markdown="renderMarkdown" + :uploads-path="pageInfo.uploadsPath" + @initialized="loadInitialContent" + @change="handleContentEditorChange" + /> + <input id="wiki_content" v-model.trim="content" type="hidden" name="wiki[content]" /> + </div> - <div class="clearfix"></div> - <div class="error-alert"></div> + <div class="clearfix"></div> + <div class="error-alert"></div> - <div class="form-text gl-text-gray-600"> - <gl-sprintf v-if="displayWikiSpecificMarkdownHelp" :message="$options.i18n.linksHelpText"> - <template #linkExample - ><code>{{ linkExample }}</code></template + <div class="form-text gl-text-gray-600"> + <gl-sprintf + v-if="displayWikiSpecificMarkdownHelp" + :message="$options.i18n.linksHelpText" > - <template - #link="// eslint-disable-next-line vue/no-template-shadow + <template #linkExample> + <code>{{ linkExample }}</code> + </template> + <template + #link="// eslint-disable-next-line vue/no-template-shadow { content }" - ><gl-link - :href="wikiSpecificMarkdownHelpPath" - target="_blank" - data-testid="wiki-markdown-help-link" - >{{ content }}</gl-link - ></template - > - </gl-sprintf> - </div> + ><gl-link + :href="wikiSpecificMarkdownHelpPath" + target="_blank" + data-testid="wiki-markdown-help-link" + >{{ content }}</gl-link + ></template + > + </gl-sprintf> + </div> + </gl-form-group> </div> </div> - <div class="form-group row"> - <div class="col-sm-2 col-form-label"> - <label class="control-label-full-width" for="wiki_message">{{ - $options.i18n.commitMessage.label - }}</label> - </div> - <div class="col-sm-10"> - <input - id="wiki_message" - v-model.trim="commitMessage" - name="wiki[message]" - type="text" - class="form-control" - data-qa-selector="wiki_message_textbox" - :placeholder="$options.i18n.commitMessage.label" - /> + + <div class="row"> + <div class="col-sm-12 row-sm-5"> + <gl-form-group :label="$options.i18n.commitMessage.label" label-for="wiki_message"> + <gl-form-input + id="wiki_message" + v-model.trim="commitMessage" + name="wiki[message]" + type="text" + class="form-control" + data-qa-selector="wiki_message_textbox" + :placeholder="$options.i18n.commitMessage.label" + /> + </gl-form-group> </div> </div> + <div class="form-actions"> <gl-button category="primary" |