diff options
Diffstat (limited to 'app/assets/javascripts/pages')
38 files changed, 487 insertions, 114 deletions
diff --git a/app/assets/javascripts/pages/admin/application_settings/index.js b/app/assets/javascripts/pages/admin/application_settings/index.js index 493c216cc6e..143d15f92cd 100644 --- a/app/assets/javascripts/pages/admin/application_settings/index.js +++ b/app/assets/javascripts/pages/admin/application_settings/index.js @@ -2,8 +2,12 @@ import initSettingsPanels from '~/settings_panels'; import projectSelect from '~/project_select'; import selfMonitor from '~/self_monitor'; import maintenanceModeSettings from '~/maintenance_mode_settings'; +import initVariableList from '~/ci_variable_list'; document.addEventListener('DOMContentLoaded', () => { + if (gon.features?.ciInstanceVariablesUi) { + initVariableList('js-instance-variables'); + } selfMonitor(); maintenanceModeSettings(); // Initialize expandable settings panels diff --git a/app/assets/javascripts/pages/admin/groups/edit/index.js b/app/assets/javascripts/pages/admin/groups/edit/index.js index ad7276132b9..a4e5df559ff 100644 --- a/app/assets/javascripts/pages/admin/groups/edit/index.js +++ b/app/assets/javascripts/pages/admin/groups/edit/index.js @@ -1,3 +1,3 @@ -import initAvatarPicker from '~/avatar_picker'; +import initFilePickers from '~/file_pickers'; -document.addEventListener('DOMContentLoaded', initAvatarPicker); +document.addEventListener('DOMContentLoaded', initFilePickers); diff --git a/app/assets/javascripts/pages/admin/groups/new/index.js b/app/assets/javascripts/pages/admin/groups/new/index.js index 6de740ee9ce..b94c999ed12 100644 --- a/app/assets/javascripts/pages/admin/groups/new/index.js +++ b/app/assets/javascripts/pages/admin/groups/new/index.js @@ -1,9 +1,10 @@ import BindInOut from '../../../../behaviors/bind_in_out'; import Group from '../../../../group'; -import initAvatarPicker from '~/avatar_picker'; +import initFilePickers from '~/file_pickers'; document.addEventListener('DOMContentLoaded', () => { BindInOut.initAll(); - new Group(); // eslint-disable-line no-new - initAvatarPicker(); + initFilePickers(); + + return new Group(); }); diff --git a/app/assets/javascripts/pages/admin/projects/index/components/delete_project_modal.vue b/app/assets/javascripts/pages/admin/projects/index/components/delete_project_modal.vue index b22fbf6b833..8bb093da771 100644 --- a/app/assets/javascripts/pages/admin/projects/index/components/delete_project_modal.vue +++ b/app/assets/javascripts/pages/admin/projects/index/components/delete_project_modal.vue @@ -92,7 +92,7 @@ export default { @submit="onSubmit" @cancel="onCancel" > - <template slot="body" slot-scope="props"> + <template #body="props"> <p v-html="props.text"></p> <p v-html="confirmationTextLabel"></p> <form ref="form" :action="deleteProjectUrl" method="post"> diff --git a/app/assets/javascripts/pages/groups/index.js b/app/assets/javascripts/pages/groups/clusters/index.js index 4d04c37caa7..4d04c37caa7 100644 --- a/app/assets/javascripts/pages/groups/index.js +++ b/app/assets/javascripts/pages/groups/clusters/index.js diff --git a/app/assets/javascripts/pages/groups/edit/index.js b/app/assets/javascripts/pages/groups/edit/index.js index f32392c9e29..33e552cd1ba 100644 --- a/app/assets/javascripts/pages/groups/edit/index.js +++ b/app/assets/javascripts/pages/groups/edit/index.js @@ -1,4 +1,4 @@ -import initAvatarPicker from '~/avatar_picker'; +import initFilePickers from '~/file_pickers'; import TransferDropdown from '~/groups/transfer_dropdown'; import initConfirmDangerModal from '~/confirm_danger_modal'; import initSettingsPanels from '~/settings_panels'; @@ -10,8 +10,7 @@ import groupsSelect from '~/groups_select'; import projectSelect from '~/project_select'; document.addEventListener('DOMContentLoaded', () => { - initAvatarPicker(); - new TransferDropdown(); // eslint-disable-line no-new + initFilePickers(); initConfirmDangerModal(); initSettingsPanels(); dirtySubmitFactory( @@ -24,4 +23,6 @@ document.addEventListener('DOMContentLoaded', () => { groupsSelect(); projectSelect(); + + return new TransferDropdown(); }); diff --git a/app/assets/javascripts/pages/groups/new/index.js b/app/assets/javascripts/pages/groups/new/index.js index 0710fefe70c..640e64b5d3e 100644 --- a/app/assets/javascripts/pages/groups/new/index.js +++ b/app/assets/javascripts/pages/groups/new/index.js @@ -1,8 +1,8 @@ import $ from 'jquery'; import BindInOut from '~/behaviors/bind_in_out'; import Group from '~/group'; -import initAvatarPicker from '~/avatar_picker'; import GroupPathValidator from './group_path_validator'; +import initFilePickers from '~/file_pickers'; document.addEventListener('DOMContentLoaded', () => { const parentId = $('#group_parent_id'); @@ -10,6 +10,7 @@ document.addEventListener('DOMContentLoaded', () => { new GroupPathValidator(); // eslint-disable-line no-new } BindInOut.initAll(); - new Group(); // eslint-disable-line no-new - initAvatarPicker(); + initFilePickers(); + + return new Group(); }); diff --git a/app/assets/javascripts/pages/groups/shared/group_details.js b/app/assets/javascripts/pages/groups/shared/group_details.js index 37b253d7c48..85daff3f60f 100644 --- a/app/assets/javascripts/pages/groups/shared/group_details.js +++ b/app/assets/javascripts/pages/groups/shared/group_details.js @@ -8,6 +8,7 @@ import NotificationsForm from '~/notifications_form'; import ProjectsList from '~/projects_list'; import ShortcutsNavigation from '~/behaviors/shortcuts/shortcuts_navigation'; import GroupTabs from './group_tabs'; +import initNamespaceStorageLimitAlert from '~/namespace_storage_limit_alert'; export default function initGroupDetails(actionName = 'show') { const newGroupChildWrapper = document.querySelector('.js-new-project-subgroup'); @@ -27,4 +28,6 @@ export default function initGroupDetails(actionName = 'show') { if (newGroupChildWrapper) { new NewGroupChild(newGroupChildWrapper); } + + initNamespaceStorageLimitAlert(); } diff --git a/app/assets/javascripts/pages/ide/index.js b/app/assets/javascripts/pages/ide/index.js index d192df3561e..15933256e75 100644 --- a/app/assets/javascripts/pages/ide/index.js +++ b/app/assets/javascripts/pages/ide/index.js @@ -1,3 +1,4 @@ import { startIde } from '~/ide/index'; +import extendStore from '~/ide/stores/extend'; -startIde(); +startIde({ extendStore }); diff --git a/app/assets/javascripts/pages/import/bitbucket/status/index.js b/app/assets/javascripts/pages/import/bitbucket/status/index.js new file mode 100644 index 00000000000..52b5adb79d1 --- /dev/null +++ b/app/assets/javascripts/pages/import/bitbucket/status/index.js @@ -0,0 +1,19 @@ +import Vue from 'vue'; +import { initStoreFromElement, initPropsFromElement } from '~/import_projects'; +import BitbucketStatusTable from '~/import_projects/components/bitbucket_status_table.vue'; + +document.addEventListener('DOMContentLoaded', () => { + const mountElement = document.getElementById('import-projects-mount-element'); + if (!mountElement) return undefined; + + const store = initStoreFromElement(mountElement); + const props = initPropsFromElement(mountElement); + + return new Vue({ + el: mountElement, + store, + render(createElement) { + return createElement(BitbucketStatusTable, { props }); + }, + }); +}); diff --git a/app/assets/javascripts/pages/import/bitbucket_server/status/components/bitbucket_server_status_table.vue b/app/assets/javascripts/pages/import/bitbucket_server/status/components/bitbucket_server_status_table.vue new file mode 100644 index 00000000000..e01c7b80e1a --- /dev/null +++ b/app/assets/javascripts/pages/import/bitbucket_server/status/components/bitbucket_server_status_table.vue @@ -0,0 +1,30 @@ +<script> +import { GlButton } from '@gitlab/ui'; +import BitbucketStatusTable from '~/import_projects/components/bitbucket_status_table.vue'; + +export default { + components: { + BitbucketStatusTable, + GlButton, + }, + props: { + providerTitle: { + type: String, + required: true, + }, + reconfigurePath: { + type: String, + required: true, + }, + }, +}; +</script> +<template> + <bitbucket-status-table :provider-title="providerTitle"> + <template #actions> + <gl-button variant="info" class="gl-ml-3" data-method="post" :href="reconfigurePath">{{ + __('Reconfigure') + }}</gl-button> + </template> + </bitbucket-status-table> +</template> diff --git a/app/assets/javascripts/pages/import/bitbucket_server/status/index.js b/app/assets/javascripts/pages/import/bitbucket_server/status/index.js new file mode 100644 index 00000000000..88455c9b7b9 --- /dev/null +++ b/app/assets/javascripts/pages/import/bitbucket_server/status/index.js @@ -0,0 +1,20 @@ +import Vue from 'vue'; +import { initStoreFromElement, initPropsFromElement } from '~/import_projects'; +import BitbucketServerStatusTable from './components/bitbucket_server_status_table.vue'; + +document.addEventListener('DOMContentLoaded', () => { + const mountElement = document.getElementById('import-projects-mount-element'); + if (!mountElement) return undefined; + + const store = initStoreFromElement(mountElement); + const props = initPropsFromElement(mountElement); + const { reconfigurePath } = mountElement.dataset; + + return new Vue({ + el: mountElement, + store, + render(createElement) { + return createElement(BitbucketServerStatusTable, { props: { ...props, reconfigurePath } }); + }, + }); +}); diff --git a/app/assets/javascripts/pages/import/fogbugz/status/index.js b/app/assets/javascripts/pages/import/fogbugz/status/index.js new file mode 100644 index 00000000000..dcd84f0faf9 --- /dev/null +++ b/app/assets/javascripts/pages/import/fogbugz/status/index.js @@ -0,0 +1,7 @@ +import mountImportProjectsTable from '~/import_projects'; + +document.addEventListener('DOMContentLoaded', () => { + const mountElement = document.getElementById('import-projects-mount-element'); + + mountImportProjectsTable(mountElement); +}); diff --git a/app/assets/javascripts/pages/import/gitlab/status/index.js b/app/assets/javascripts/pages/import/gitlab/status/index.js new file mode 100644 index 00000000000..dcd84f0faf9 --- /dev/null +++ b/app/assets/javascripts/pages/import/gitlab/status/index.js @@ -0,0 +1,7 @@ +import mountImportProjectsTable from '~/import_projects'; + +document.addEventListener('DOMContentLoaded', () => { + const mountElement = document.getElementById('import-projects-mount-element'); + + mountImportProjectsTable(mountElement); +}); diff --git a/app/assets/javascripts/pages/ldap/omniauth_callbacks/index.js b/app/assets/javascripts/pages/ldap/omniauth_callbacks/index.js index edd7e38471b..e93def5323f 100644 --- a/app/assets/javascripts/pages/ldap/omniauth_callbacks/index.js +++ b/app/assets/javascripts/pages/ldap/omniauth_callbacks/index.js @@ -1,3 +1,3 @@ -import initU2F from '../../../shared/sessions/u2f'; +import { mount2faAuthentication } from '~/authentication/mount_2fa'; -document.addEventListener('DOMContentLoaded', initU2F); +document.addEventListener('DOMContentLoaded', mount2faAuthentication); diff --git a/app/assets/javascripts/pages/omniauth_callbacks/index.js b/app/assets/javascripts/pages/omniauth_callbacks/index.js index c2c069d1ca8..e93def5323f 100644 --- a/app/assets/javascripts/pages/omniauth_callbacks/index.js +++ b/app/assets/javascripts/pages/omniauth_callbacks/index.js @@ -1,3 +1,3 @@ -import initU2F from '../../shared/sessions/u2f'; +import { mount2faAuthentication } from '~/authentication/mount_2fa'; -document.addEventListener('DOMContentLoaded', initU2F); +document.addEventListener('DOMContentLoaded', mount2faAuthentication); diff --git a/app/assets/javascripts/pages/profiles/two_factor_auths/index.js b/app/assets/javascripts/pages/profiles/two_factor_auths/index.js index 95936c2d1db..1aeba6669ee 100644 --- a/app/assets/javascripts/pages/profiles/two_factor_auths/index.js +++ b/app/assets/javascripts/pages/profiles/two_factor_auths/index.js @@ -1,6 +1,5 @@ -import $ from 'jquery'; -import U2FRegister from '~/u2f/register'; import { parseBoolean } from '~/lib/utils/common_utils'; +import { mount2faRegistration } from '~/authentication/mount_2fa'; document.addEventListener('DOMContentLoaded', () => { const twoFactorNode = document.querySelector('.js-two-factor-auth'); @@ -12,6 +11,5 @@ document.addEventListener('DOMContentLoaded', () => { if (flashAlert) flashAlert.insertAdjacentHTML('beforeend', button); } - const u2fRegister = new U2FRegister($('#js-register-u2f'), gon.u2f); - u2fRegister.start(); + mount2faRegistration(); }); diff --git a/app/assets/javascripts/pages/projects/clusters/index.js b/app/assets/javascripts/pages/projects/clusters/index.js new file mode 100644 index 00000000000..4d04c37caa7 --- /dev/null +++ b/app/assets/javascripts/pages/projects/clusters/index.js @@ -0,0 +1,5 @@ +import initCreateCluster from '~/create_cluster/init_create_cluster'; + +document.addEventListener('DOMContentLoaded', () => { + initCreateCluster(document, gon); +}); diff --git a/app/assets/javascripts/pages/projects/edit/index.js b/app/assets/javascripts/pages/projects/edit/index.js index c9dbe576c4b..9fb07917f9b 100644 --- a/app/assets/javascripts/pages/projects/edit/index.js +++ b/app/assets/javascripts/pages/projects/edit/index.js @@ -4,12 +4,12 @@ import setupTransferEdit from '~/transfer_edit'; import initConfirmDangerModal from '~/confirm_danger_modal'; import mountBadgeSettings from '~/pages/shared/mount_badge_settings'; import dirtySubmitFactory from '~/dirty_submit/dirty_submit_factory'; -import initAvatarPicker from '~/avatar_picker'; +import initFilePickers from '~/file_pickers'; import initProjectLoadingSpinner from '../shared/save_project_loader'; import initProjectPermissionsSettings from '../shared/permissions'; document.addEventListener('DOMContentLoaded', () => { - initAvatarPicker(); + initFilePickers(); initConfirmDangerModal(); initSettingsPanels(); mountBadgeSettings(PROJECT_BADGE); diff --git a/app/assets/javascripts/pages/projects/environments/metrics/index.js b/app/assets/javascripts/pages/projects/environments/metrics/index.js index 31ec4e29ad2..d3028aec313 100644 --- a/app/assets/javascripts/pages/projects/environments/metrics/index.js +++ b/app/assets/javascripts/pages/projects/environments/metrics/index.js @@ -1,3 +1,3 @@ -import monitoringBundle from '~/monitoring/monitoring_bundle_with_alerts'; +import monitoringApp from '~/monitoring/monitoring_app'; -document.addEventListener('DOMContentLoaded', monitoringBundle); +document.addEventListener('DOMContentLoaded', monitoringApp); diff --git a/app/assets/javascripts/pages/projects/graphs/charts/index.js b/app/assets/javascripts/pages/projects/graphs/charts/index.js index 803f4e37705..03504fba1ae 100644 --- a/app/assets/javascripts/pages/projects/graphs/charts/index.js +++ b/app/assets/javascripts/pages/projects/graphs/charts/index.js @@ -1,10 +1,12 @@ import Vue from 'vue'; import { __ } from '~/locale'; import { GlColumnChart } from '@gitlab/ui/dist/charts'; +import CodeCoverage from '../components/code_coverage.vue'; import SeriesDataMixin from './series_data_mixin'; document.addEventListener('DOMContentLoaded', () => { const languagesContainer = document.getElementById('js-languages-chart'); + const codeCoverageContainer = document.getElementById('js-code-coverage-chart'); const monthContainer = document.getElementById('js-month-chart'); const weekdayContainer = document.getElementById('js-weekday-chart'); const hourContainer = document.getElementById('js-hour-chart'); @@ -59,6 +61,18 @@ document.addEventListener('DOMContentLoaded', () => { // eslint-disable-next-line no-new new Vue({ + el: codeCoverageContainer, + render(h) { + return h(CodeCoverage, { + props: { + graphEndpoint: codeCoverageContainer.dataset?.graphEndpoint, + }, + }); + }, + }); + + // eslint-disable-next-line no-new + new Vue({ el: monthContainer, components: { GlColumnChart, diff --git a/app/assets/javascripts/pages/projects/graphs/components/code_coverage.vue b/app/assets/javascripts/pages/projects/graphs/components/code_coverage.vue new file mode 100644 index 00000000000..af8fb032c22 --- /dev/null +++ b/app/assets/javascripts/pages/projects/graphs/components/code_coverage.vue @@ -0,0 +1,177 @@ +<script> +import { GlAlert, GlDropdown, GlDropdownItem, GlIcon, GlSprintf } from '@gitlab/ui'; +import { GlAreaChart } from '@gitlab/ui/dist/charts'; +import dateFormat from 'dateformat'; +import axios from '~/lib/utils/axios_utils'; +import { get } from 'lodash'; + +import { __ } from '~/locale'; + +export default { + components: { + GlAlert, + GlAreaChart, + GlDropdown, + GlDropdownItem, + GlIcon, + GlSprintf, + }, + props: { + graphEndpoint: { + type: String, + required: true, + }, + }, + data() { + return { + dailyCoverageData: [], + hasFetchError: false, + isLoading: true, + selectedCoverageIndex: 0, + tooltipTitle: '', + coveragePercentage: '', + chartOptions: { + yAxis: { + name: __('Bi-weekly code coverage'), + type: 'value', + min: 0, + max: 100, + }, + xAxis: { + name: '', + type: 'category', + }, + }, + }; + }, + computed: { + hasData() { + return this.dailyCoverageData.length > 0; + }, + isReady() { + return !this.isLoading && !this.hasFetchError; + }, + canShowData() { + return this.isReady && this.hasData; + }, + noDataAvailable() { + return this.isReady && !this.hasData; + }, + selectedDailyCoverage() { + return this.hasData && this.dailyCoverageData[this.selectedCoverageIndex]; + }, + selectedDailyCoverageName() { + return this.selectedDailyCoverage?.group_name; + }, + formattedData() { + if (this.selectedDailyCoverage?.data) { + return this.selectedDailyCoverage.data.map(value => [ + dateFormat(value.date, 'mmm dd'), + value.coverage, + ]); + } + + // If the fetching failed, we return an empty array which + // allow the graph to render while empty + return []; + }, + chartData() { + return [ + { + // The default string 'data' will get shown in the legend if we fail to fetch the data + name: this.canShowData ? this.selectedDailyCoverageName : __('data'), + data: this.formattedData, + type: 'line', + smooth: true, + }, + ]; + }, + }, + created() { + axios + .get(this.graphEndpoint) + .then(({ data }) => { + this.dailyCoverageData = data; + }) + .catch(() => { + this.hasFetchError = true; + }) + .finally(() => { + this.isLoading = false; + }); + }, + methods: { + setSelectedCoverage(index) { + this.selectedCoverageIndex = index; + }, + formatTooltipText(params) { + this.tooltipTitle = params.value; + this.coveragePercentage = get(params, 'seriesData[0].data[1]', ''); + }, + }, + height: 200, +}; +</script> + +<template> + <div> + <div class="gl-mt-3 gl-mb-3"> + <gl-alert + v-if="hasFetchError" + variant="danger" + :title="s__('Code Coverage|Couldn\'t fetch the code coverage data')" + :dismissible="false" + /> + <gl-alert + v-if="noDataAvailable" + variant="info" + :title="s__('Code Coverage| Empty code coverage data')" + :dismissible="false" + > + <span> + {{ __('It seems that there is currently no available data for code coverage') }} + </span> + </gl-alert> + <gl-dropdown v-if="canShowData" :text="selectedDailyCoverageName"> + <gl-dropdown-item + v-for="({ group_name }, index) in dailyCoverageData" + :key="index" + :value="group_name" + @click="setSelectedCoverage(index)" + > + <div class="gl-display-flex"> + <gl-icon + v-if="index === selectedCoverageIndex" + name="mobile-issue-close" + class="gl-absolute" + /> + <span class="gl-display-flex align-items-center ml-4"> + {{ group_name }} + </span> + </div> + </gl-dropdown-item> + </gl-dropdown> + </div> + <gl-area-chart + v-if="!isLoading" + :height="$options.height" + :data="chartData" + :option="chartOptions" + :format-tooltip-text="formatTooltipText" + > + <template v-if="canShowData" #tooltipTitle> + {{ tooltipTitle }} + </template> + <template v-if="canShowData" #tooltipContent> + <gl-sprintf :message="__('Code Coverage: %{coveragePercentage}%{percentSymbol}')"> + <template #coveragePercentage> + {{ coveragePercentage }} + </template> + <template #percentSymbol> + % + </template> + </gl-sprintf> + </template> + </gl-area-chart> + </div> +</template> diff --git a/app/assets/javascripts/pages/projects/index.js b/app/assets/javascripts/pages/projects/index.js index 190d0806c28..8e0af018b61 100644 --- a/app/assets/javascripts/pages/projects/index.js +++ b/app/assets/javascripts/pages/projects/index.js @@ -1,10 +1,7 @@ import Project from './project'; import ShortcutsNavigation from '../../behaviors/shortcuts/shortcuts_navigation'; -import initCreateCluster from '~/create_cluster/init_create_cluster'; document.addEventListener('DOMContentLoaded', () => { - initCreateCluster(document, gon); - new Project(); // eslint-disable-line no-new new ShortcutsNavigation(); // eslint-disable-line no-new }); diff --git a/app/assets/javascripts/pages/projects/issues/index/index.js b/app/assets/javascripts/pages/projects/issues/index/index.js index e8e0cda2139..a66b665d152 100644 --- a/app/assets/javascripts/pages/projects/issues/index/index.js +++ b/app/assets/javascripts/pages/projects/issues/index/index.js @@ -9,6 +9,7 @@ import { FILTERED_SEARCH } from '~/pages/constants'; import { ISSUABLE_INDEX } from '~/pages/projects/constants'; import initIssuablesList from '~/issuables_list'; import initManualOrdering from '~/manual_ordering'; +import { showLearnGitLabIssuesPopover } from '~/onboarding_issues'; document.addEventListener('DOMContentLoaded', () => { IssuableFilteredSearchTokenKeys.addExtraTokensForIssues(); @@ -24,4 +25,5 @@ document.addEventListener('DOMContentLoaded', () => { initManualOrdering(); initIssuablesList(); + showLearnGitLabIssuesPopover(); }); diff --git a/app/assets/javascripts/pages/projects/labels/components/promote_label_modal.vue b/app/assets/javascripts/pages/projects/labels/components/promote_label_modal.vue index 3b26047455d..08078fa6b62 100644 --- a/app/assets/javascripts/pages/projects/labels/components/promote_label_modal.vue +++ b/app/assets/javascripts/pages/projects/labels/components/promote_label_modal.vue @@ -90,7 +90,7 @@ export default { footer-primary-button-variant="warning" @submit="onSubmit" > - <div slot="title" class="modal-title-with-label" v-html="title">{{ title }}</div> + <div slot="title" class="modal-title-with-label" v-html="title"></div> {{ text }} </gl-modal> diff --git a/app/assets/javascripts/pages/projects/merge_requests/show/index.js b/app/assets/javascripts/pages/projects/merge_requests/show/index.js index ddc648702f1..4708970efef 100644 --- a/app/assets/javascripts/pages/projects/merge_requests/show/index.js +++ b/app/assets/javascripts/pages/projects/merge_requests/show/index.js @@ -1,4 +1,5 @@ import initMrNotes from '~/mr_notes'; +import { initReviewBar } from '~/batch_comments'; import initSidebarBundle from '~/sidebar/sidebar_bundle'; import initShow from '../init_merge_request_show'; @@ -8,4 +9,5 @@ document.addEventListener('DOMContentLoaded', () => { initSidebarBundle(); } initMrNotes(); + initReviewBar(); }); diff --git a/app/assets/javascripts/pages/projects/new/index.js b/app/assets/javascripts/pages/projects/new/index.js index 097403ba9e2..e17059dd55a 100644 --- a/app/assets/javascripts/pages/projects/new/index.js +++ b/app/assets/javascripts/pages/projects/new/index.js @@ -1,7 +1,46 @@ import initProjectVisibilitySelector from '../../../project_visibility'; import initProjectNew from '../../../projects/project_new'; +import { __ } from '~/locale'; +import createFlash from '~/flash'; +import Tracking from '~/tracking'; document.addEventListener('DOMContentLoaded', () => { initProjectVisibilitySelector(); initProjectNew.bindEvents(); + + const { category, property } = gon.tracking_data ?? { category: 'projects:new' }; + const hasNewCreateProjectUi = 'newCreateProjectUi' in gon?.features; + + if (!hasNewCreateProjectUi) { + // Setting additional tracking for HAML template + + Array.from( + document.querySelectorAll('.project-edit-container [data-experiment-track-label]'), + ).forEach(node => + node.addEventListener('click', event => { + const { experimentTrackLabel: label } = event.currentTarget.dataset; + Tracking.event(category, 'click_tab', { property, label }); + }), + ); + } else { + import( + /* webpackChunkName: 'experiment_new_project_creation' */ '../../../projects/experiment_new_project_creation' + ) + .then(m => { + const el = document.querySelector('.js-experiment-new-project-creation'); + + if (!el) { + return; + } + + const config = { + hasErrors: 'hasErrors' in el.dataset, + isCiCdAvailable: 'isCiCdAvailable' in el.dataset, + }; + m.default(el, config); + }) + .catch(() => { + createFlash(__('An error occurred while loading project creation UI')); + }); + } }); diff --git a/app/assets/javascripts/pages/projects/pipelines/index/index.js b/app/assets/javascripts/pages/projects/pipelines/index/index.js index bbad3238ec4..2c37d7da4a7 100644 --- a/app/assets/javascripts/pages/projects/pipelines/index/index.js +++ b/app/assets/javascripts/pages/projects/pipelines/index/index.js @@ -51,6 +51,7 @@ document.addEventListener( ciLintPath: this.dataset.ciLintPath, resetCachePath: this.dataset.resetCachePath, projectId: this.dataset.projectId, + params: JSON.parse(this.dataset.params), }, }); }, 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 ab32fe18972..7181332a1d6 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 @@ -46,7 +46,11 @@ export default { allowedVisibilityOptions: { type: Array, required: false, - default: () => [0, 10, 20], + default: () => [ + visibilityOptions.PRIVATE, + visibilityOptions.INTERNAL, + visibilityOptions.PUBLIC, + ], }, lfsAvailable: { type: Boolean, @@ -118,16 +122,14 @@ export default { const defaults = { visibilityOptions, visibilityLevel: visibilityOptions.PUBLIC, - // TODO: Change all of these to use the visibilityOptions constants - // https://gitlab.com/gitlab-org/gitlab/-/issues/214667 - issuesAccessLevel: 20, - repositoryAccessLevel: 20, - forkingAccessLevel: 20, - mergeRequestsAccessLevel: 20, - buildsAccessLevel: 20, - wikiAccessLevel: 20, - snippetsAccessLevel: 20, - pagesAccessLevel: 20, + issuesAccessLevel: featureAccessLevel.EVERYONE, + repositoryAccessLevel: featureAccessLevel.EVERYONE, + forkingAccessLevel: featureAccessLevel.EVERYONE, + mergeRequestsAccessLevel: featureAccessLevel.EVERYONE, + buildsAccessLevel: featureAccessLevel.EVERYONE, + wikiAccessLevel: featureAccessLevel.EVERYONE, + snippetsAccessLevel: featureAccessLevel.EVERYONE, + pagesAccessLevel: featureAccessLevel.EVERYONE, metricsDashboardAccessLevel: featureAccessLevel.PROJECT_MEMBERS, containerRegistryEnabled: true, lfsEnabled: true, @@ -180,7 +182,7 @@ export default { }, repositoryEnabled() { - return this.repositoryAccessLevel > 0; + return this.repositoryAccessLevel > featureAccessLevel.NOT_ENABLED; }, visibilityLevelDescription() { @@ -206,40 +208,70 @@ export default { visibilityLevel(value, oldValue) { if (value === visibilityOptions.PRIVATE) { // when private, features are restricted to "only team members" - this.issuesAccessLevel = Math.min(10, this.issuesAccessLevel); - this.repositoryAccessLevel = Math.min(10, this.repositoryAccessLevel); - this.mergeRequestsAccessLevel = Math.min(10, this.mergeRequestsAccessLevel); - this.buildsAccessLevel = Math.min(10, this.buildsAccessLevel); - this.wikiAccessLevel = Math.min(10, this.wikiAccessLevel); - this.snippetsAccessLevel = Math.min(10, this.snippetsAccessLevel); - this.metricsDashboardAccessLevel = Math.min(10, this.metricsDashboardAccessLevel); - if (this.pagesAccessLevel === 20) { + this.issuesAccessLevel = Math.min( + featureAccessLevel.PROJECT_MEMBERS, + this.issuesAccessLevel, + ); + this.repositoryAccessLevel = Math.min( + featureAccessLevel.PROJECT_MEMBERS, + this.repositoryAccessLevel, + ); + this.mergeRequestsAccessLevel = Math.min( + featureAccessLevel.PROJECT_MEMBERS, + this.mergeRequestsAccessLevel, + ); + this.buildsAccessLevel = Math.min( + featureAccessLevel.PROJECT_MEMBERS, + this.buildsAccessLevel, + ); + this.wikiAccessLevel = Math.min(featureAccessLevel.PROJECT_MEMBERS, this.wikiAccessLevel); + this.snippetsAccessLevel = Math.min( + featureAccessLevel.PROJECT_MEMBERS, + this.snippetsAccessLevel, + ); + this.metricsDashboardAccessLevel = Math.min( + featureAccessLevel.PROJECT_MEMBERS, + this.metricsDashboardAccessLevel, + ); + if (this.pagesAccessLevel === featureAccessLevel.EVERYONE) { // When from Internal->Private narrow access for only members - this.pagesAccessLevel = 10; + this.pagesAccessLevel = featureAccessLevel.PROJECT_MEMBERS; } this.highlightChanges(); } else if (oldValue === visibilityOptions.PRIVATE) { // if changing away from private, make enabled features more permissive - if (this.issuesAccessLevel > 0) this.issuesAccessLevel = 20; - if (this.repositoryAccessLevel > 0) this.repositoryAccessLevel = 20; - if (this.mergeRequestsAccessLevel > 0) this.mergeRequestsAccessLevel = 20; - if (this.buildsAccessLevel > 0) this.buildsAccessLevel = 20; - if (this.wikiAccessLevel > 0) this.wikiAccessLevel = 20; - if (this.snippetsAccessLevel > 0) this.snippetsAccessLevel = 20; - if (this.pagesAccessLevel === 10) this.pagesAccessLevel = 20; - if (this.metricsDashboardAccessLevel === 10) this.metricsDashboardAccessLevel = 20; + if (this.issuesAccessLevel > featureAccessLevel.NOT_ENABLED) + this.issuesAccessLevel = featureAccessLevel.EVERYONE; + if (this.repositoryAccessLevel > featureAccessLevel.NOT_ENABLED) + this.repositoryAccessLevel = featureAccessLevel.EVERYONE; + if (this.mergeRequestsAccessLevel > featureAccessLevel.NOT_ENABLED) + this.mergeRequestsAccessLevel = featureAccessLevel.EVERYONE; + if (this.buildsAccessLevel > featureAccessLevel.NOT_ENABLED) + this.buildsAccessLevel = featureAccessLevel.EVERYONE; + if (this.wikiAccessLevel > featureAccessLevel.NOT_ENABLED) + this.wikiAccessLevel = featureAccessLevel.EVERYONE; + if (this.snippetsAccessLevel > featureAccessLevel.NOT_ENABLED) + this.snippetsAccessLevel = featureAccessLevel.EVERYONE; + if (this.pagesAccessLevel === featureAccessLevel.PROJECT_MEMBERS) + this.pagesAccessLevel = featureAccessLevel.EVERYONE; + if (this.metricsDashboardAccessLevel === featureAccessLevel.PROJECT_MEMBERS) + this.metricsDashboardAccessLevel = featureAccessLevel.EVERYONE; this.highlightChanges(); } }, issuesAccessLevel(value, oldValue) { - if (value === 0) toggleHiddenClassBySelector('.issues-feature', true); - else if (oldValue === 0) toggleHiddenClassBySelector('.issues-feature', false); + if (value === featureAccessLevel.NOT_ENABLED) + toggleHiddenClassBySelector('.issues-feature', true); + else if (oldValue === featureAccessLevel.NOT_ENABLED) + toggleHiddenClassBySelector('.issues-feature', false); }, mergeRequestsAccessLevel(value, oldValue) { - if (value === 0) toggleHiddenClassBySelector('.merge-requests-feature', true); - else if (oldValue === 0) toggleHiddenClassBySelector('.merge-requests-feature', false); + if (value === featureAccessLevel.NOT_ENABLED) + toggleHiddenClassBySelector('.merge-requests-feature', true); + else if (oldValue === featureAccessLevel.NOT_ENABLED) + toggleHiddenClassBySelector('.merge-requests-feature', false); }, }, diff --git a/app/assets/javascripts/pages/projects/show/index.js b/app/assets/javascripts/pages/projects/show/index.js index 6ae10c98058..3c44053e2b2 100644 --- a/app/assets/javascripts/pages/projects/show/index.js +++ b/app/assets/javascripts/pages/projects/show/index.js @@ -1,4 +1,6 @@ import $ from 'jquery'; +import 'jquery.waitforimages'; + import initBlob from '~/blob_edit/blob_bundle'; import ShortcutsNavigation from '~/behaviors/shortcuts/shortcuts_navigation'; import NotificationsForm from '~/notifications_form'; @@ -12,9 +14,12 @@ import initReadMore from '~/read_more'; import leaveByUrl from '~/namespaces/leave_by_url'; import Star from '../../../star'; import notificationsDropdown from '../../../notifications_dropdown'; +import initNamespaceStorageLimitAlert from '~/namespace_storage_limit_alert'; +import { showLearnGitLabProjectPopover } from '~/onboarding_issues'; document.addEventListener('DOMContentLoaded', () => { initReadMore(); + initNamespaceStorageLimitAlert(); new Star(); // eslint-disable-line no-new notificationsDropdown(); new ShortcutsNavigation(); // eslint-disable-line no-new @@ -55,4 +60,6 @@ document.addEventListener('DOMContentLoaded', () => { throw e; }); } + + showLearnGitLabProjectPopover(); }); diff --git a/app/assets/javascripts/pages/projects/tree/show/index.js b/app/assets/javascripts/pages/projects/tree/show/index.js index 16d71379e31..0d1d32317fe 100644 --- a/app/assets/javascripts/pages/projects/tree/show/index.js +++ b/app/assets/javascripts/pages/projects/tree/show/index.js @@ -1,4 +1,6 @@ import $ from 'jquery'; +import 'jquery.waitforimages'; + import Vue from 'vue'; import initBlob from '~/blob_edit/blob_bundle'; import commitPipelineStatus from '~/projects/tree/components/commit_pipeline_status_component.vue'; diff --git a/app/assets/javascripts/pages/projects/wikis/index.js b/app/assets/javascripts/pages/projects/wikis/index.js index f5fd84d69ac..9c75531ca40 100644 --- a/app/assets/javascripts/pages/projects/wikis/index.js +++ b/app/assets/javascripts/pages/projects/wikis/index.js @@ -1,41 +1,3 @@ -import $ from 'jquery'; -import Vue from 'vue'; -import Translate from '~/vue_shared/translate'; -import csrf from '~/lib/utils/csrf'; -import ShortcutsWiki from '~/behaviors/shortcuts/shortcuts_wiki'; -import Wikis from './wikis'; -import ZenMode from '../../../zen_mode'; -import GLForm from '../../../gl_form'; -import deleteWikiModal from './components/delete_wiki_modal.vue'; +import initWikis from '~/pages/shared/wikis'; -document.addEventListener('DOMContentLoaded', () => { - new Wikis(); // eslint-disable-line no-new - new ShortcutsWiki(); // eslint-disable-line no-new - new ZenMode(); // eslint-disable-line no-new - new GLForm($('.wiki-form')); // eslint-disable-line no-new - - const deleteWikiModalWrapperEl = document.getElementById('delete-wiki-modal-wrapper'); - - if (deleteWikiModalWrapperEl) { - Vue.use(Translate); - - const { deleteWikiUrl, pageTitle } = deleteWikiModalWrapperEl.dataset; - - // eslint-disable-next-line no-new - new Vue({ - el: deleteWikiModalWrapperEl, - data: { - deleteWikiUrl: '', - }, - render(createElement) { - return createElement(deleteWikiModal, { - props: { - pageTitle, - deleteWikiUrl, - csrfToken: csrf.token, - }, - }); - }, - }); - } -}); +document.addEventListener('DOMContentLoaded', initWikis); diff --git a/app/assets/javascripts/pages/sessions/index.js b/app/assets/javascripts/pages/sessions/index.js index c2c069d1ca8..e93def5323f 100644 --- a/app/assets/javascripts/pages/sessions/index.js +++ b/app/assets/javascripts/pages/sessions/index.js @@ -1,3 +1,3 @@ -import initU2F from '../../shared/sessions/u2f'; +import { mount2faAuthentication } from '~/authentication/mount_2fa'; -document.addEventListener('DOMContentLoaded', initU2F); +document.addEventListener('DOMContentLoaded', mount2faAuthentication); diff --git a/app/assets/javascripts/pages/sessions/new/oauth_remember_me.js b/app/assets/javascripts/pages/sessions/new/oauth_remember_me.js index 191221a48cd..8d2d5d41f6a 100644 --- a/app/assets/javascripts/pages/sessions/new/oauth_remember_me.js +++ b/app/assets/javascripts/pages/sessions/new/oauth_remember_me.js @@ -5,13 +5,12 @@ import { mergeUrlParams, removeParams } from '~/lib/utils/url_utility'; * OAuth-based login buttons have a separate "remember me" checkbox. * * Toggling this checkbox adds/removes a `remember_me` parameter to the - * login buttons' href, which is passed on to the omniauth callback. + * login buttons' parent form action, which is passed on to the omniauth callback. */ export default class OAuthRememberMe { constructor(opts = {}) { this.container = opts.container || ''; - this.loginLinkSelector = '.oauth-login'; } bindEvents() { @@ -22,12 +21,13 @@ export default class OAuthRememberMe { const rememberMe = $(event.target).is(':checked'); $('.oauth-login', this.container).each((i, element) => { - const href = $(element).attr('href'); + const $form = $(element).parent('form'); + const href = $form.attr('action'); if (rememberMe) { - $(element).attr('href', mergeUrlParams({ remember_me: 1 }, href)); + $form.attr('action', mergeUrlParams({ remember_me: 1 }, href)); } else { - $(element).attr('href', removeParams(['remember_me'], href)); + $form.attr('action', removeParams(['remember_me'], href)); } }); } diff --git a/app/assets/javascripts/pages/sessions/new/preserve_url_fragment.js b/app/assets/javascripts/pages/sessions/new/preserve_url_fragment.js index e617fecaa0f..1d47a9aed47 100644 --- a/app/assets/javascripts/pages/sessions/new/preserve_url_fragment.js +++ b/app/assets/javascripts/pages/sessions/new/preserve_url_fragment.js @@ -12,7 +12,7 @@ export default function preserveUrlFragment(fragment = '') { // Append the fragment to all sign-in/sign-up form actions so it is preserved when the user is // eventually redirected back to the originally requested URL. - const forms = document.querySelectorAll('#signin-container form'); + const forms = document.querySelectorAll('#signin-container .tab-content form'); Array.prototype.forEach.call(forms, form => { const actionWithFragment = setUrlFragment(form.getAttribute('action'), `#${normalFragment}`); form.setAttribute('action', actionWithFragment); @@ -20,13 +20,13 @@ export default function preserveUrlFragment(fragment = '') { // Append a redirect_fragment query param to all oauth provider links. The redirect_fragment // query param will be available in the omniauth callback upon successful authentication - const anchors = document.querySelectorAll('#signin-container a.oauth-login'); - Array.prototype.forEach.call(anchors, anchor => { + const oauthForms = document.querySelectorAll('#signin-container .omniauth-container form'); + Array.prototype.forEach.call(oauthForms, oauthForm => { const newHref = mergeUrlParams( { redirect_fragment: normalFragment }, - anchor.getAttribute('href'), + oauthForm.getAttribute('action'), ); - anchor.setAttribute('href', newHref); + oauthForm.setAttribute('action', newHref); }); } } diff --git a/app/assets/javascripts/pages/projects/wikis/components/delete_wiki_modal.vue b/app/assets/javascripts/pages/shared/wikis/components/delete_wiki_modal.vue index 580cca49b5e..580cca49b5e 100644 --- a/app/assets/javascripts/pages/projects/wikis/components/delete_wiki_modal.vue +++ b/app/assets/javascripts/pages/shared/wikis/components/delete_wiki_modal.vue diff --git a/app/assets/javascripts/pages/shared/wikis/index.js b/app/assets/javascripts/pages/shared/wikis/index.js new file mode 100644 index 00000000000..5e23b9bab4e --- /dev/null +++ b/app/assets/javascripts/pages/shared/wikis/index.js @@ -0,0 +1,41 @@ +import $ from 'jquery'; +import Vue from 'vue'; +import Translate from '~/vue_shared/translate'; +import csrf from '~/lib/utils/csrf'; +import ShortcutsWiki from '~/behaviors/shortcuts/shortcuts_wiki'; +import Wikis from './wikis'; +import ZenMode from '../../../zen_mode'; +import GLForm from '../../../gl_form'; +import deleteWikiModal from './components/delete_wiki_modal.vue'; + +export default () => { + new Wikis(); // eslint-disable-line no-new + new ShortcutsWiki(); // eslint-disable-line no-new + new ZenMode(); // eslint-disable-line no-new + new GLForm($('.wiki-form')); // eslint-disable-line no-new + + const deleteWikiModalWrapperEl = document.getElementById('delete-wiki-modal-wrapper'); + + if (deleteWikiModalWrapperEl) { + Vue.use(Translate); + + const { deleteWikiUrl, pageTitle } = deleteWikiModalWrapperEl.dataset; + + // eslint-disable-next-line no-new + new Vue({ + el: deleteWikiModalWrapperEl, + data: { + deleteWikiUrl: '', + }, + render(createElement) { + return createElement(deleteWikiModal, { + props: { + pageTitle, + deleteWikiUrl, + csrfToken: csrf.token, + }, + }); + }, + }); + } +}; diff --git a/app/assets/javascripts/pages/projects/wikis/wikis.js b/app/assets/javascripts/pages/shared/wikis/wikis.js index ed67219383b..ed67219383b 100644 --- a/app/assets/javascripts/pages/projects/wikis/wikis.js +++ b/app/assets/javascripts/pages/shared/wikis/wikis.js |