diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2022-03-18 23:02:30 +0300 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2022-03-18 23:02:30 +0300 |
commit | 41fe97390ceddf945f3d967b8fdb3de4c66b7dea (patch) | |
tree | 9c8d89a8624828992f06d892cd2f43818ff5dcc8 /app/assets/javascripts/clusters_list | |
parent | 0804d2dc31052fb45a1efecedc8e06ce9bc32862 (diff) |
Add latest changes from gitlab-org/gitlab@14-9-stable-eev14.9.0-rc42
Diffstat (limited to 'app/assets/javascripts/clusters_list')
16 files changed, 428 insertions, 349 deletions
diff --git a/app/assets/javascripts/clusters_list/components/agent_table.vue b/app/assets/javascripts/clusters_list/components/agent_table.vue index 61c4904aacf..1144ce68e2c 100644 --- a/app/assets/javascripts/clusters_list/components/agent_table.vue +++ b/app/assets/javascripts/clusters_list/components/agent_table.vue @@ -1,5 +1,13 @@ <script> -import { GlLink, GlTable, GlIcon, GlSprintf, GlTooltip, GlPopover } from '@gitlab/ui'; +import { + GlLink, + GlTable, + GlIcon, + GlSprintf, + GlTooltip, + GlTooltipDirective, + GlPopover, +} from '@gitlab/ui'; import TimeAgoTooltip from '~/vue_shared/components/time_ago_tooltip.vue'; import timeagoMixin from '~/vue_shared/mixins/timeago'; import { helpPagePath } from '~/helpers/help_page_helper'; @@ -19,12 +27,18 @@ export default { TimeAgoTooltip, DeleteAgentButton, }, + directives: { + GlTooltip: GlTooltipDirective, + }, mixins: [timeagoMixin], AGENT_STATUSES, troubleshootingLink: helpPagePath('user/clusters/agent/troubleshooting'), versionUpdateLink: helpPagePath('user/clusters/agent/install/index', { anchor: 'update-the-agent-version', }), + configHelpLink: helpPagePath('user/clusters/agent/install/index', { + anchor: 'create-an-agent-without-configuration-file', + }), inject: ['gitlabVersion'], props: { agents: { @@ -256,7 +270,16 @@ export default { {{ getAgentConfigPath(item.name) }} </gl-link> - <span v-else>{{ getAgentConfigPath(item.name) }}</span> + <span v-else + >{{ $options.i18n.defaultConfigText }} + <gl-link + v-gl-tooltip + :href="$options.configHelpLink" + :title="$options.i18n.defaultConfigTooltip" + :aria-label="$options.i18n.defaultConfigTooltip" + class="gl-vertical-align-middle" + ><gl-icon name="question" :size="14" /></gl-link + ></span> </span> </template> diff --git a/app/assets/javascripts/clusters_list/components/agent_token.vue b/app/assets/javascripts/clusters_list/components/agent_token.vue new file mode 100644 index 00000000000..eab3fc3ed63 --- /dev/null +++ b/app/assets/javascripts/clusters_list/components/agent_token.vue @@ -0,0 +1,109 @@ +<script> +import { GlAlert, GlFormInputGroup, GlLink, GlSprintf } from '@gitlab/ui'; +import { helpPagePath } from '~/helpers/help_page_helper'; +import ModalCopyButton from '~/vue_shared/components/modal_copy_button.vue'; +import CodeBlock from '~/vue_shared/components/code_block.vue'; +import { generateAgentRegistrationCommand } from '../clusters_util'; +import { I18N_AGENT_TOKEN } from '../constants'; + +export default { + i18n: I18N_AGENT_TOKEN, + basicInstallPath: helpPagePath('user/clusters/agent/install/index', { + anchor: 'install-the-agent-into-the-cluster', + }), + advancedInstallPath: helpPagePath('user/clusters/agent/install/index', { + anchor: 'advanced-installation', + }), + components: { + GlAlert, + CodeBlock, + GlFormInputGroup, + GlLink, + GlSprintf, + ModalCopyButton, + }, + inject: ['kasAddress'], + props: { + agentToken: { + required: true, + type: String, + }, + modalId: { + required: true, + type: String, + }, + }, + computed: { + agentRegistrationCommand() { + return generateAgentRegistrationCommand(this.agentToken, this.kasAddress); + }, + }, +}; +</script> + +<template> + <div> + <p> + <strong>{{ $options.i18n.tokenTitle }}</strong> + </p> + + <p> + <gl-sprintf :message="$options.i18n.tokenBody"> + <template #link="{ content }"> + <gl-link :href="$options.basicInstallPath" target="_blank"> {{ content }}</gl-link> + </template> + </gl-sprintf> + </p> + + <p> + <gl-alert + :title="$options.i18n.tokenSingleUseWarningTitle" + variant="warning" + :dismissible="false" + > + {{ $options.i18n.tokenSingleUseWarningBody }} + </gl-alert> + </p> + + <p> + <gl-form-input-group readonly :value="agentToken" :select-on-click="true"> + <template #append> + <modal-copy-button + :text="agentToken" + :title="$options.i18n.copyToken" + :modal-id="modalId" + /> + </template> + </gl-form-input-group> + </p> + + <p> + <strong>{{ $options.i18n.basicInstallTitle }}</strong> + </p> + + <p> + {{ $options.i18n.basicInstallBody }} + </p> + + <p class="gl-display-flex gl-align-items-flex-start"> + <code-block class="gl-w-full" :code="agentRegistrationCommand" /> + <modal-copy-button + :title="$options.i18n.copyCommand" + :text="agentRegistrationCommand" + :modal-id="modalId" + /> + </p> + + <p> + <strong>{{ $options.i18n.advancedInstallTitle }}</strong> + </p> + + <p> + <gl-sprintf :message="$options.i18n.advancedInstallBody"> + <template #link="{ content }"> + <gl-link :href="$options.advancedInstallPath" target="_blank"> {{ content }}</gl-link> + </template> + </gl-sprintf> + </p> + </div> +</template> diff --git a/app/assets/javascripts/clusters_list/components/agents.vue b/app/assets/javascripts/clusters_list/components/agents.vue index bf096f53e9d..70b9b8ac3c9 100644 --- a/app/assets/javascripts/clusters_list/components/agents.vue +++ b/app/assets/javascripts/clusters_list/components/agents.vue @@ -116,9 +116,6 @@ export default { }, }, methods: { - reloadAgents() { - this.$apollo.queries.agents.refetch(); - }, nextPage() { this.cursor = { first: MAX_LIST_COUNT, diff --git a/app/assets/javascripts/clusters_list/components/available_agents_dropdown.vue b/app/assets/javascripts/clusters_list/components/available_agents_dropdown.vue index 1630d0d5c92..662cf2a7e36 100644 --- a/app/assets/javascripts/clusters_list/components/available_agents_dropdown.vue +++ b/app/assets/javascripts/clusters_list/components/available_agents_dropdown.vue @@ -1,5 +1,11 @@ <script> -import { GlDropdown, GlDropdownItem } from '@gitlab/ui'; +import { + GlDropdown, + GlDropdownItem, + GlDropdownDivider, + GlSearchBoxByType, + GlSprintf, +} from '@gitlab/ui'; import { I18N_AVAILABLE_AGENTS_DROPDOWN } from '../constants'; export default { @@ -8,6 +14,9 @@ export default { components: { GlDropdown, GlDropdownItem, + GlDropdownDivider, + GlSearchBoxByType, + GlSprintf, }, props: { isRegistering: { @@ -22,6 +31,7 @@ export default { data() { return { selectedAgent: null, + searchTerm: '', }; }, computed: { @@ -34,22 +44,45 @@ export default { return this.selectedAgent; }, + shouldRenderCreateButton() { + return this.searchTerm && !this.availableAgents.includes(this.searchTerm); + }, + filteredResults() { + const lowerCasedSearchTerm = this.searchTerm.toLowerCase(); + return this.availableAgents.filter((resultString) => + resultString.toLowerCase().includes(lowerCasedSearchTerm), + ); + }, }, methods: { selectAgent(agent) { this.$emit('agentSelected', agent); this.selectedAgent = agent; + this.clearSearch(); }, isSelected(agent) { return this.selectedAgent === agent; }, + clearSearch() { + this.searchTerm = ''; + }, + focusSearch() { + this.$refs.searchInput.focusInput(); + }, + handleShow() { + this.clearSearch(); + this.focusSearch(); + }, }, }; </script> <template> - <gl-dropdown :text="dropdownText" :loading="isRegistering"> + <gl-dropdown :text="dropdownText" :loading="isRegistering" @shown="handleShow"> + <template #header> + <gl-search-box-by-type ref="searchInput" v-model.trim="searchTerm" /> + </template> <gl-dropdown-item - v-for="agent in availableAgents" + v-for="agent in filteredResults" :key="agent" :is-checked="isSelected(agent)" is-check-item @@ -57,5 +90,16 @@ export default { > {{ agent }} </gl-dropdown-item> + <gl-dropdown-item v-if="!filteredResults.length" ref="noMatchingResults">{{ + $options.i18n.noResults + }}</gl-dropdown-item> + <template v-if="shouldRenderCreateButton"> + <gl-dropdown-divider /> + <gl-dropdown-item data-testid="create-config-button" @click="selectAgent(searchTerm)"> + <gl-sprintf :message="$options.i18n.createButton"> + <template #searchTerm>{{ searchTerm }}</template> + </gl-sprintf> + </gl-dropdown-item> + </template> </gl-dropdown> </template> diff --git a/app/assets/javascripts/clusters_list/components/clusters.vue b/app/assets/javascripts/clusters_list/components/clusters.vue index 7fb3aa3ff7e..59cfdde731d 100644 --- a/app/assets/javascripts/clusters_list/components/clusters.vue +++ b/app/assets/javascripts/clusters_list/components/clusters.vue @@ -6,7 +6,7 @@ import { GlPagination, GlDeprecatedSkeletonLoading as GlSkeletonLoading, GlSprintf, - GlTable, + GlTableLite, GlTooltipDirective, } from '@gitlab/ui'; import { mapState, mapActions } from 'vuex'; @@ -27,7 +27,7 @@ export default { GlPagination, GlSkeletonLoading, GlSprintf, - GlTable, + GlTableLite, NodeErrorHelpText, ClustersEmptyState, }, @@ -229,7 +229,7 @@ export default { <section v-else> <ancestor-notice /> - <gl-table + <gl-table-lite v-if="hasClusters" :items="clusters" :fields="fields" @@ -326,7 +326,7 @@ export default { {{ value }} </gl-badge> </template> - </gl-table> + </gl-table-lite> <clusters-empty-state v-else :is-child-component="isChildComponent" /> diff --git a/app/assets/javascripts/clusters_list/components/clusters_actions.vue b/app/assets/javascripts/clusters_list/components/clusters_actions.vue index 5b8dc74b84f..ccb973f1eb8 100644 --- a/app/assets/javascripts/clusters_list/components/clusters_actions.vue +++ b/app/assets/javascripts/clusters_list/components/clusters_actions.vue @@ -1,5 +1,6 @@ <script> import { + GlButton, GlDropdown, GlDropdownItem, GlModalDirective, @@ -14,6 +15,7 @@ export default { i18n: CLUSTERS_ACTIONS, INSTALL_AGENT_MODAL_ID, components: { + GlButton, GlDropdown, GlDropdownItem, GlDropdownDivider, @@ -23,11 +25,27 @@ export default { GlModalDirective, GlTooltip: GlTooltipDirective, }, - inject: ['newClusterPath', 'addClusterPath', 'canAddCluster'], + inject: [ + 'newClusterPath', + 'addClusterPath', + 'canAddCluster', + 'displayClusterAgents', + 'certificateBasedClustersEnabled', + ], computed: { tooltip() { - const { connectWithAgent, dropdownDisabledHint } = this.$options.i18n; - return this.canAddCluster ? connectWithAgent : dropdownDisabledHint; + const { connectWithAgent, connectExistingCluster, dropdownDisabledHint } = this.$options.i18n; + + if (!this.canAddCluster) { + return dropdownDisabledHint; + } else if (this.displayClusterAgents) { + return connectWithAgent; + } + + return connectExistingCluster; + }, + shouldTriggerModal() { + return this.canAddCluster && this.displayClusterAgents; }, }, }; @@ -36,25 +54,29 @@ export default { <template> <div class="nav-controls gl-ml-auto"> <gl-dropdown + v-if="certificateBasedClustersEnabled" ref="dropdown" - v-gl-modal-directive="canAddCluster && $options.INSTALL_AGENT_MODAL_ID" + v-gl-modal-directive="shouldTriggerModal && $options.INSTALL_AGENT_MODAL_ID" v-gl-tooltip="tooltip" category="primary" variant="confirm" :text="$options.i18n.actionsButton" :disabled="!canAddCluster" - split + :split="displayClusterAgents" right > - <gl-dropdown-section-header>{{ $options.i18n.agent }}</gl-dropdown-section-header> - <gl-dropdown-item - v-gl-modal-directive="$options.INSTALL_AGENT_MODAL_ID" - data-testid="connect-new-agent-link" - > - {{ $options.i18n.connectWithAgent }} - </gl-dropdown-item> - <gl-dropdown-divider /> - <gl-dropdown-section-header>{{ $options.i18n.certificate }}</gl-dropdown-section-header> + <template v-if="displayClusterAgents"> + <gl-dropdown-section-header>{{ $options.i18n.agent }}</gl-dropdown-section-header> + <gl-dropdown-item + v-gl-modal-directive="$options.INSTALL_AGENT_MODAL_ID" + data-testid="connect-new-agent-link" + > + {{ $options.i18n.connectWithAgent }} + </gl-dropdown-item> + <gl-dropdown-divider /> + <gl-dropdown-section-header>{{ $options.i18n.certificate }}</gl-dropdown-section-header> + </template> + <gl-dropdown-item :href="newClusterPath" data-testid="new-cluster-link" @click.stop> {{ $options.i18n.createNewCluster }} </gl-dropdown-item> @@ -62,5 +84,15 @@ export default { {{ $options.i18n.connectExistingCluster }} </gl-dropdown-item> </gl-dropdown> + <gl-button + v-else + v-gl-modal-directive="$options.INSTALL_AGENT_MODAL_ID" + v-gl-tooltip="tooltip" + :disabled="!canAddCluster" + category="primary" + variant="confirm" + > + {{ $options.i18n.connectWithAgent }} + </gl-button> </div> </template> diff --git a/app/assets/javascripts/clusters_list/components/clusters_empty_state.vue b/app/assets/javascripts/clusters_list/components/clusters_empty_state.vue index ce601de57bd..76bec05cfc7 100644 --- a/app/assets/javascripts/clusters_list/components/clusters_empty_state.vue +++ b/app/assets/javascripts/clusters_list/components/clusters_empty_state.vue @@ -13,7 +13,7 @@ export default { GlSprintf, GlAlert, }, - inject: ['emptyStateHelpText', 'clustersEmptyStateImage', 'newClusterPath'], + inject: ['emptyStateHelpText', 'clustersEmptyStateImage', 'addClusterPath'], props: { isChildComponent: { default: false, @@ -57,7 +57,7 @@ export default { category="primary" variant="confirm" :disabled="!canAddCluster" - :href="newClusterPath" + :href="addClusterPath" > {{ $options.i18n.buttonText }} </gl-button> diff --git a/app/assets/javascripts/clusters_list/components/clusters_main_view.vue b/app/assets/javascripts/clusters_list/components/clusters_main_view.vue index 7dd5ece9b8e..aab6d3dc1f0 100644 --- a/app/assets/javascripts/clusters_list/components/clusters_main_view.vue +++ b/app/assets/javascripts/clusters_list/components/clusters_main_view.vue @@ -3,11 +3,13 @@ import { GlTabs, GlTab } from '@gitlab/ui'; import Tracking from '~/tracking'; import { CLUSTERS_TABS, + CERTIFICATE_TAB, MAX_CLUSTERS_LIST, MAX_LIST_COUNT, AGENT, EVENT_LABEL_TABS, EVENT_ACTIONS_CHANGE, + AGENT_TAB, } from '../constants'; import Agents from './agents.vue'; import InstallAgentModal from './install_agent_modal.vue'; @@ -27,8 +29,8 @@ export default { Agents, InstallAgentModal, }, - CLUSTERS_TABS, mixins: [trackingMixin], + inject: ['displayClusterAgents', 'certificateBasedClustersEnabled'], props: { defaultBranchName: { default: '.noBranch', @@ -42,13 +44,30 @@ export default { maxAgents: MAX_CLUSTERS_LIST, }; }, + computed: { + availableTabs() { + const clusterTabs = this.displayClusterAgents ? CLUSTERS_TABS : [CERTIFICATE_TAB]; + return this.certificateBasedClustersEnabled ? clusterTabs : [AGENT_TAB]; + }, + }, + watch: { + selectedTabIndex: { + handler(val) { + this.onTabChange(val); + }, + immediate: true, + }, + }, methods: { - onTabChange(tabName) { - this.selectedTabIndex = CLUSTERS_TABS.findIndex((tab) => tab.queryParamValue === tabName); - this.maxAgents = tabName === AGENT ? MAX_LIST_COUNT : MAX_CLUSTERS_LIST; + setSelectedTab(tabName) { + this.selectedTabIndex = this.availableTabs.findIndex( + (tab) => tab.queryParamValue === tabName, + ); }, - trackTabChange(tab) { - const tabName = CLUSTERS_TABS[tab].queryParamValue; + onTabChange(tab) { + const tabName = this.availableTabs[tab].queryParamValue; + + this.maxAgents = tabName === AGENT ? MAX_LIST_COUNT : MAX_CLUSTERS_LIST; this.track(EVENT_ACTIONS_CHANGE, { property: tabName }); }, }, @@ -61,10 +80,9 @@ export default { sync-active-tab-with-query-params nav-class="gl-flex-grow-1 gl-align-items-center" lazy - @input="trackTabChange" > <gl-tab - v-for="(tab, idx) in $options.CLUSTERS_TABS" + v-for="(tab, idx) in availableTabs" :key="idx" :title="tab.title" :query-param-value="tab.queryParamValue" @@ -74,7 +92,7 @@ export default { :is="tab.component" :default-branch-name="defaultBranchName" data-testid="clusters-tab-component" - @changeTab="onTabChange" + @changeTab="setSelectedTab" /> </gl-tab> diff --git a/app/assets/javascripts/clusters_list/components/delete_agent_button.vue b/app/assets/javascripts/clusters_list/components/delete_agent_button.vue index 6588d304d5c..6f2c353a67b 100644 --- a/app/assets/javascripts/clusters_list/components/delete_agent_button.vue +++ b/app/assets/javascripts/clusters_list/components/delete_agent_button.vue @@ -116,7 +116,7 @@ export default { this.$toast.show(this.error || successMessage); - this.$refs.modal.hide(); + this.$refs.modal?.hide(); } }, deleteAgentMutation() { diff --git a/app/assets/javascripts/clusters_list/components/install_agent_modal.vue b/app/assets/javascripts/clusters_list/components/install_agent_modal.vue index 8fc0a66cd7e..ae0affe4c8b 100644 --- a/app/assets/javascripts/clusters_list/components/install_agent_modal.vue +++ b/app/assets/javascripts/clusters_list/components/install_agent_modal.vue @@ -1,18 +1,7 @@ <script> -import { - GlAlert, - GlButton, - GlFormGroup, - GlFormInputGroup, - GlLink, - GlModal, - GlSprintf, -} from '@gitlab/ui'; +import { GlAlert, GlButton, GlFormGroup, GlLink, GlModal, GlSprintf } from '@gitlab/ui'; import { helpPagePath } from '~/helpers/help_page_helper'; -import ModalCopyButton from '~/vue_shared/components/modal_copy_button.vue'; -import CodeBlock from '~/vue_shared/components/code_block.vue'; import Tracking from '~/tracking'; -import { generateAgentRegistrationCommand } from '../clusters_util'; import { INSTALL_AGENT_MODAL_ID, I18N_AGENT_MODAL, @@ -30,39 +19,32 @@ import createAgentToken from '../graphql/mutations/create_agent_token.mutation.g import getAgentsQuery from '../graphql/queries/get_agents.query.graphql'; import agentConfigurations from '../graphql/queries/agent_configurations.query.graphql'; import AvailableAgentsDropdown from './available_agents_dropdown.vue'; +import AgentToken from './agent_token.vue'; const trackingMixin = Tracking.mixin({ label: EVENT_LABEL_MODAL }); export default { modalId: INSTALL_AGENT_MODAL_ID, + i18n: I18N_AGENT_MODAL, EVENT_ACTIONS_OPEN, EVENT_ACTIONS_CLICK, EVENT_LABEL_MODAL, - basicInstallPath: helpPagePath('user/clusters/agent/install/index', { - anchor: 'install-the-agent-into-the-cluster', - }), - advancedInstallPath: helpPagePath('user/clusters/agent/install/index', { - anchor: 'advanced-installation', - }), enableKasPath: helpPagePath('administration/clusters/kas'), - installAgentPath: helpPagePath('user/clusters/agent/install/index'), registerAgentPath: helpPagePath('user/clusters/agent/install/index', { anchor: 'register-an-agent-with-gitlab', }), components: { AvailableAgentsDropdown, - CodeBlock, + AgentToken, GlAlert, GlButton, GlFormGroup, - GlFormInputGroup, GlLink, GlModal, GlSprintf, - ModalCopyButton, }, mixins: [trackingMixin], - inject: ['projectPath', 'kasAddress', 'emptyStateImage'], + inject: ['projectPath', 'emptyStateImage'], props: { defaultBranchName: { default: '.noBranch', @@ -109,13 +91,10 @@ export default { return !this.registering && this.agentName !== null; }, canCancel() { - return !this.registered && !this.registering && this.isAgentRegistrationModal; + return !this.registered && !this.registering && !this.kasDisabled; }, canRegister() { - return !this.registered && this.isAgentRegistrationModal; - }, - agentRegistrationCommand() { - return generateAgentRegistrationCommand(this.agentToken, this.kasAddress); + return !this.registered && !this.kasDisabled; }, getAgentsQueryVariables() { return { @@ -125,32 +104,20 @@ export default { projectPath: this.projectPath, }; }, - i18n() { - return I18N_AGENT_MODAL[this.modalType]; - }, + repositoryPath() { return `/${this.projectPath}`; }, modalType() { - return !this.availableAgents?.length && !this.registered - ? MODAL_TYPE_EMPTY - : MODAL_TYPE_REGISTER; + return this.kasDisabled ? MODAL_TYPE_EMPTY : MODAL_TYPE_REGISTER; }, modalSize() { - return this.isEmptyStateModal ? 'sm' : 'md'; - }, - isEmptyStateModal() { - return this.modalType === MODAL_TYPE_EMPTY; - }, - isAgentRegistrationModal() { - return this.modalType === MODAL_TYPE_REGISTER; - }, - isKasEnabledInEmptyStateModal() { - return this.isEmptyStateModal && !this.kasDisabled; + return this.kasDisabled ? 'sm' : 'md'; }, }, methods: { setAgentName(name) { + this.error = null; this.agentName = name; this.track(EVENT_ACTIONS_SELECT); }, @@ -194,13 +161,13 @@ export default { return createClusterAgent; }); }, - createAgentTokenMutation(agendId) { + createAgentTokenMutation(agentId) { return this.$apollo .mutate({ mutation: createAgentToken, variables: { input: { - clusterAgentId: agendId, + clusterAgentId: agentId, name: this.agentName, }, }, @@ -244,7 +211,7 @@ export default { if (error) { this.error = error.message; } else { - this.error = this.i18n.unknownError; + this.error = this.$options.i18n.unknownError; } } finally { this.registering = false; @@ -258,22 +225,21 @@ export default { <gl-modal ref="modal" :modal-id="$options.modalId" - :title="i18n.modalTitle" + :title="$options.i18n.modalTitle" :size="modalSize" static lazy @hidden="resetModal" @show="track($options.EVENT_ACTIONS_OPEN, { property: modalType })" > - <template v-if="isAgentRegistrationModal"> + <template v-if="!kasDisabled"> <template v-if="!registered"> - <p> - <strong>{{ i18n.selectAgentTitle }}</strong> - </p> - - <p class="gl-mb-0">{{ i18n.selectAgentBody }}</p> - <p> - <gl-link :href="$options.registerAgentPath"> {{ i18n.learnMoreLink }}</gl-link> + <p class="gl-mb-0"> + <gl-sprintf :message="$options.i18n.modalBody"> + <template #link="{ content }"> + <gl-link :href="repositoryPath">{{ content }}</gl-link> + </template> + </gl-sprintf> </p> <form> @@ -287,90 +253,36 @@ export default { </gl-form-group> </form> - <p v-if="error"> - <gl-alert :title="i18n.registrationErrorTitle" variant="danger" :dismissible="false"> - {{ error }} - </gl-alert> - </p> - </template> - - <template v-else> - <p> - <strong>{{ i18n.tokenTitle }}</strong> - </p> - <p> - <gl-sprintf :message="i18n.tokenBody"> - <template #link="{ content }"> - <gl-link :href="$options.basicInstallPath" target="_blank"> {{ content }}</gl-link> - </template> - </gl-sprintf> + <gl-link :href="$options.registerAgentPath"> {{ $options.i18n.learnMoreLink }}</gl-link> </p> - <p> - <gl-alert :title="i18n.tokenSingleUseWarningTitle" variant="warning" :dismissible="false"> - {{ i18n.tokenSingleUseWarningBody }} + <p v-if="error"> + <gl-alert + :title="$options.i18n.registrationErrorTitle" + variant="danger" + :dismissible="false" + > + {{ error }} </gl-alert> </p> - - <p> - <gl-form-input-group readonly :value="agentToken" :select-on-click="true"> - <template #append> - <modal-copy-button - :text="agentToken" - :title="i18n.copyToken" - :modal-id="$options.modalId" - /> - </template> - </gl-form-input-group> - </p> - - <p> - <strong>{{ i18n.basicInstallTitle }}</strong> - </p> - - <p> - {{ i18n.basicInstallBody }} - </p> - - <p> - <code-block :code="agentRegistrationCommand" /> - </p> - - <p> - <strong>{{ i18n.advancedInstallTitle }}</strong> - </p> - - <p> - <gl-sprintf :message="i18n.advancedInstallBody"> - <template #link="{ content }"> - <gl-link :href="$options.advancedInstallPath" target="_blank"> {{ content }}</gl-link> - </template> - </gl-sprintf> - </p> </template> + + <agent-token v-else :agent-token="agentToken" :modal-id="$options.modalId" /> </template> <template v-else> <div class="gl-text-center gl-mb-5"> - <img :alt="i18n.altText" :src="emptyStateImage" height="100" /> + <img :alt="$options.i18n.altText" :src="emptyStateImage" height="100" /> </div> <p v-if="kasDisabled"> - <gl-sprintf :message="i18n.enableKasText"> + <gl-sprintf :message="$options.i18n.enableKasText"> <template #link="{ content }"> <gl-link :href="$options.enableKasPath">{{ content }}</gl-link> </template> </gl-sprintf> </p> - - <p v-else> - <gl-sprintf :message="i18n.modalBody"> - <template #link="{ content }"> - <gl-link :href="$options.installAgentPath">{{ content }}</gl-link> - </template> - </gl-sprintf> - </p> </template> <template #modal-footer> @@ -382,7 +294,7 @@ export default { :data-track-label="$options.EVENT_LABEL_MODAL" data-track-property="close" @click="closeModal" - >{{ i18n.close }} + >{{ $options.i18n.close }} </gl-button> <gl-button @@ -391,7 +303,7 @@ export default { :data-track-label="$options.EVENT_LABEL_MODAL" data-track-property="cancel" @click="closeModal" - >{{ i18n.cancel }} + >{{ $options.i18n.cancel }} </gl-button> <gl-button @@ -403,25 +315,16 @@ export default { :data-track-label="$options.EVENT_LABEL_MODAL" data-track-property="register" @click="registerAgent" - >{{ i18n.registerAgentButton }} + >{{ $options.i18n.registerAgentButton }} </gl-button> <gl-button - v-if="isEmptyStateModal" + v-if="kasDisabled" :data-track-action="$options.EVENT_ACTIONS_CLICK" :data-track-label="$options.EVENT_LABEL_MODAL" data-track-property="done" @click="closeModal" - >{{ i18n.done }} - </gl-button> - - <gl-button - v-if="isKasEnabledInEmptyStateModal" - :href="repositoryPath" - variant="confirm" - category="primary" - data-testid="agent-primary-button" - >{{ i18n.primaryButton }} + >{{ $options.i18n.close }} </gl-button> </template> </gl-modal> diff --git a/app/assets/javascripts/clusters_list/constants.js b/app/assets/javascripts/clusters_list/constants.js index 5cf6fd050a1..c914ee518b2 100644 --- a/app/assets/javascripts/clusters_list/constants.js +++ b/app/assets/javascripts/clusters_list/constants.js @@ -75,74 +75,74 @@ export const I18N_AGENT_TABLE = { neverConnectedText: s__('ClusterAgents|Never'), versionMismatchTitle: s__('ClusterAgents|Agent version mismatch'), versionMismatchText: s__( - "ClusterAgents|The Agent version do not match each other across your cluster's pods. This can happen when a new Agent version was just deployed and Kubernetes is shutting down the old pods.", + "ClusterAgents|The agent version do not match each other across your cluster's pods. This can happen when a new agent version was just deployed and Kubernetes is shutting down the old pods.", ), versionOutdatedTitle: s__('ClusterAgents|Agent version update required'), versionOutdatedText: s__( - 'ClusterAgents|Your Agent version is out of sync with your GitLab version (v%{version}), which might cause compatibility problems. Update the Agent installed on your cluster to the most recent version.', + 'ClusterAgents|Your agent version is out of sync with your GitLab version (v%{version}), which might cause compatibility problems. Update the agent installed on your cluster to the most recent version.', ), versionMismatchOutdatedTitle: s__('ClusterAgents|Agent version mismatch and update'), - viewDocsText: s__('ClusterAgents|How to update the Agent?'), + viewDocsText: s__('ClusterAgents|How to update an agent?'), + defaultConfigText: s__('ClusterAgents|Default configuration'), + defaultConfigTooltip: s__('ClusterAgents|What is default configuration?'), }; -export const I18N_AGENT_MODAL = { - agent_registration: { - registerAgentButton: s__('ClusterAgents|Register'), - close: __('Close'), - cancel: __('Cancel'), - - modalTitle: s__('ClusterAgents|Connect a cluster through the Agent'), - selectAgentTitle: s__('ClusterAgents|Select an agent to register with GitLab'), - selectAgentBody: s__( - 'ClusterAgents|Register an agent to generate a token that will be used to install the agent on your cluster in the next step.', - ), - learnMoreLink: s__('ClusterAgents|How to register an agent?'), +export const I18N_AGENT_TOKEN = { + copyToken: s__('ClusterAgents|Copy token'), + copyCommand: s__('ClusterAgents|Copy command'), + tokenTitle: s__('ClusterAgents|Registration token'), - copyToken: s__('ClusterAgents|Copy token'), - tokenTitle: s__('ClusterAgents|Registration token'), - tokenBody: s__( - `ClusterAgents|The registration token will be used to connect the agent on your cluster to GitLab. %{linkStart}What are registration tokens?%{linkEnd}`, - ), + tokenBody: s__( + `ClusterAgents|The registration token will be used to connect the agent on your cluster to GitLab. %{linkStart}What are registration tokens?%{linkEnd}`, + ), + tokenSingleUseWarningTitle: s__( + 'ClusterAgents|You cannot see this token again after you close this window.', + ), + tokenSingleUseWarningBody: s__( + `ClusterAgents|The recommended installation method includes the token. If you want to follow the advanced installation method provided in the docs, make sure you save the token value before you close this window.`, + ), - tokenSingleUseWarningTitle: s__( - 'ClusterAgents|You cannot see this token again after you close this window.', - ), - tokenSingleUseWarningBody: s__( - `ClusterAgents|The recommended installation method includes the token. If you want to follow the advanced installation method provided in the docs, make sure you save the token value before you close this window.`, - ), + basicInstallTitle: s__('ClusterAgents|Recommended installation method'), + basicInstallBody: __( + `Open a CLI and connect to the cluster you want to install the agent in. Use this installation method to minimize any manual steps. The token is already included in the command.`, + ), - basicInstallTitle: s__('ClusterAgents|Recommended installation method'), - basicInstallBody: __( - `Open a CLI and connect to the cluster you want to install the agent in. Use this installation method to minimize any manual steps. The token is already included in the command.`, - ), + advancedInstallTitle: s__('ClusterAgents|Advanced installation methods'), + advancedInstallBody: s__( + 'ClusterAgents|For the advanced installation method %{linkStart}see the documentation%{linkEnd}.', + ), +}; - advancedInstallTitle: s__('ClusterAgents|Advanced installation methods'), - advancedInstallBody: s__( - 'ClusterAgents|For the advanced installation method %{linkStart}see the documentation%{linkEnd}.', - ), +export const I18N_AGENT_MODAL = { + registerAgentButton: s__('ClusterAgents|Register'), + close: __('Close'), + cancel: __('Cancel'), - registrationErrorTitle: s__('ClusterAgents|Failed to register an agent'), - unknownError: s__('ClusterAgents|An unknown error occurred. Please try again.'), - }, - empty_state: { - modalTitle: s__('ClusterAgents|Connect your cluster through the Agent'), - modalBody: s__( - "ClusterAgents|To install a new agent, first add the agent's configuration file to this repository. %{linkStart}Learn more about installing GitLab Agent.%{linkEnd}", - ), - enableKasText: s__( - "ClusterAgents|Your instance doesn't have the %{linkStart}GitLab Agent Server (KAS)%{linkEnd} set up. Ask a GitLab Administrator to install it.", - ), - altText: s__('ClusterAgents|GitLab Agent for Kubernetes'), - primaryButton: s__('ClusterAgents|Go to the repository files'), - done: __('Cancel'), - }, + modalTitle: s__('ClusterAgents|Connect a cluster through an agent'), + modalBody: s__( + 'ClusterAgents|Add an agent configuration file to %{linkStart}this repository%{linkEnd} and select it, or create a new one to register with GitLab:', + ), + enableKasText: s__( + "ClusterAgents|Your instance doesn't have the %{linkStart}GitLab Agent Server (KAS)%{linkEnd} set up. Ask a GitLab Administrator to install it.", + ), + altText: s__('ClusterAgents|GitLab Agent for Kubernetes'), + learnMoreLink: s__('ClusterAgents|How do I register an agent?'), + copyToken: s__('ClusterAgents|Copy token'), + tokenTitle: s__('ClusterAgents|Registration token'), + tokenBody: s__( + `ClusterAgents|The registration token will be used to connect the agent on your cluster to GitLab. %{linkStart}What are registration tokens?%{linkEnd}`, + ), + registrationErrorTitle: s__('ClusterAgents|Failed to register an agent'), + unknownError: s__('ClusterAgents|An unknown error occurred. Please try again.'), }; export const KAS_DISABLED_ERROR = 'Gitlab::Kas::Client::ConfigurationError'; export const I18N_AVAILABLE_AGENTS_DROPDOWN = { - selectAgent: s__('ClusterAgents|Select an agent'), - registeringAgent: s__('ClusterAgents|Registering Agent'), + selectAgent: s__('ClusterAgents|Select an agent or enter a name to create new'), + registeringAgent: s__('ClusterAgents|Registering agent'), + noResults: __('No matching results'), + createButton: s__('ClusterAgents|Create agent: %{searchTerm}'), }; export const AGENT_STATUSES = { @@ -197,8 +197,8 @@ export const I18N_CLUSTERS_EMPTY_STATE = { export const AGENT_CARD_INFO = { tabName: 'agent', - title: sprintf(s__('ClusterAgents|%{number} of %{total} Agents')), - emptyTitle: s__('ClusterAgents|No Agents'), + title: sprintf(s__('ClusterAgents|%{number} of %{total} agents')), + emptyTitle: s__('ClusterAgents|No agents'), tooltip: { label: s__('ClusterAgents|Recommended'), title: s__('ClusterAgents|GitLab Agent'), @@ -209,7 +209,7 @@ export const AGENT_CARD_INFO = { ), link: helpPagePath('user/clusters/agent/index'), }, - actionText: s__('ClusterAgents|Install new Agent'), + actionText: s__('ClusterAgents|Install a new agent'), footerText: sprintf(s__('ClusterAgents|View all %{number} agents')), installAgentDisabledHint: s__( 'ClusterAgents|Requires a Maintainer or greater role to install new agents', @@ -232,28 +232,29 @@ export const CERTIFICATE_BASED_CARD_INFO = { export const MAX_CLUSTERS_LIST = 6; -export const CLUSTERS_TABS = [ - { - title: s__('ClusterAgents|All'), - component: 'ClustersViewAll', - queryParamValue: 'all', - }, - { - title: s__('ClusterAgents|Agent'), - component: 'agents', - queryParamValue: 'agent', - }, - { - title: s__('ClusterAgents|Certificate'), - component: 'clusters', - queryParamValue: 'certificate_based', - }, -]; +export const ALL_TAB = { + title: s__('ClusterAgents|All'), + component: 'ClustersViewAll', + queryParamValue: 'all', +}; + +export const AGENT_TAB = { + title: s__('ClusterAgents|Agent'), + component: 'agents', + queryParamValue: 'agent', +}; +export const CERTIFICATE_TAB = { + title: s__('ClusterAgents|Certificate'), + component: 'clusters', + queryParamValue: 'certificate_based', +}; + +export const CLUSTERS_TABS = [ALL_TAB, AGENT_TAB, CERTIFICATE_TAB]; export const CLUSTERS_ACTIONS = { actionsButton: s__('ClusterAgents|Actions'), createNewCluster: s__('ClusterAgents|Create a new cluster'), - connectWithAgent: s__('ClusterAgents|Connect with Agent'), + connectWithAgent: s__('ClusterAgents|Connect with an agent'), connectExistingCluster: s__('ClusterAgents|Connect with a certificate'), agent: s__('ClusterAgents|Agent'), certificate: s__('ClusterAgents|Certificate'), diff --git a/app/assets/javascripts/clusters_list/graphql/cache_update.js b/app/assets/javascripts/clusters_list/graphql/cache_update.js index 6476b7a6c2f..e68f6a378c0 100644 --- a/app/assets/javascripts/clusters_list/graphql/cache_update.js +++ b/app/assets/javascripts/clusters_list/graphql/cache_update.js @@ -1,5 +1,4 @@ import produce from 'immer'; -import { getAgentConfigPath } from '../clusters_util'; export const hasErrors = ({ errors = [] }) => errors?.length; @@ -12,17 +11,8 @@ export function addAgentToStore(store, createClusterAgent, query, variables) { }); const data = produce(sourceData, (draftData) => { - const configuration = { - id: clusterAgent.id, - name: clusterAgent.name, - path: getAgentConfigPath(clusterAgent.name), - webPath: clusterAgent.webPath, - __typename: 'TreeEntry', - }; - draftData.project.clusterAgents.nodes.push(clusterAgent); draftData.project.clusterAgents.count += 1; - draftData.project.repository.tree.trees.nodes.push(configuration); }); store.writeQuery({ diff --git a/app/assets/javascripts/clusters_list/graphql/queries/get_agents.query.graphql b/app/assets/javascripts/clusters_list/graphql/queries/get_agents.query.graphql index f8efb6683f6..7743ffba5de 100644 --- a/app/assets/javascripts/clusters_list/graphql/queries/get_agents.query.graphql +++ b/app/assets/javascripts/clusters_list/graphql/queries/get_agents.query.graphql @@ -7,9 +7,7 @@ query getAgents( $first: Int $last: Int $afterAgent: String - $afterTree: String $beforeAgent: String - $beforeTree: String ) { project(fullPath: $projectPath) { id @@ -27,17 +25,13 @@ query getAgents( repository { tree(path: ".gitlab/agents", ref: $defaultBranchName) { - trees(first: $first, last: $last, after: $afterTree, before: $beforeTree) { + trees { nodes { id name path webPath } - - pageInfo { - ...PageInfo - } } } } diff --git a/app/assets/javascripts/clusters_list/index.js b/app/assets/javascripts/clusters_list/index.js index 6148483dcb0..27eebc9d891 100644 --- a/app/assets/javascripts/clusters_list/index.js +++ b/app/assets/javascripts/clusters_list/index.js @@ -1,13 +1,63 @@ import { GlToast } from '@gitlab/ui'; import Vue from 'vue'; import VueApollo from 'vue-apollo'; -import loadClusters from './load_clusters'; -import loadMainView from './load_main_view'; +import { parseBoolean } from '~/lib/utils/common_utils'; +import createDefaultClient from '~/lib/graphql'; +import ClustersMainView from './components/clusters_main_view.vue'; +import { createStore } from './store'; Vue.use(GlToast); Vue.use(VueApollo); export default () => { - loadClusters(Vue); - loadMainView(Vue, VueApollo); + const el = document.querySelector('.js-clusters-main-view'); + + if (!el) { + return null; + } + + const defaultClient = createDefaultClient(); + + const { + emptyStateImage, + defaultBranchName, + projectPath, + kasAddress, + newClusterPath, + addClusterPath, + emptyStateHelpText, + clustersEmptyStateImage, + canAddCluster, + canAdminCluster, + gitlabVersion, + displayClusterAgents, + certificateBasedClustersEnabled, + } = el.dataset; + + return new Vue({ + el, + apolloProvider: new VueApollo({ defaultClient }), + provide: { + emptyStateImage, + projectPath, + kasAddress, + newClusterPath, + addClusterPath, + emptyStateHelpText, + clustersEmptyStateImage, + canAddCluster: parseBoolean(canAddCluster), + canAdminCluster: parseBoolean(canAdminCluster), + gitlabVersion, + displayClusterAgents: parseBoolean(displayClusterAgents), + certificateBasedClustersEnabled: parseBoolean(certificateBasedClustersEnabled), + }, + store: createStore(el.dataset), + render(createElement) { + return createElement(ClustersMainView, { + props: { + defaultBranchName, + }, + }); + }, + }); }; diff --git a/app/assets/javascripts/clusters_list/load_clusters.js b/app/assets/javascripts/clusters_list/load_clusters.js deleted file mode 100644 index 1bb3ea546b2..00000000000 --- a/app/assets/javascripts/clusters_list/load_clusters.js +++ /dev/null @@ -1,25 +0,0 @@ -import Clusters from './components/clusters.vue'; -import { createStore } from './store'; - -export default (Vue) => { - const el = document.querySelector('#js-clusters-list-app'); - - if (!el) { - return null; - } - - const { emptyStateHelpText, newClusterPath, clustersEmptyStateImage } = el.dataset; - - return new Vue({ - el, - provide: { - emptyStateHelpText, - newClusterPath, - clustersEmptyStateImage, - }, - store: createStore(el.dataset), - render(createElement) { - return createElement(Clusters); - }, - }); -}; diff --git a/app/assets/javascripts/clusters_list/load_main_view.js b/app/assets/javascripts/clusters_list/load_main_view.js deleted file mode 100644 index d52b1d4a64d..00000000000 --- a/app/assets/javascripts/clusters_list/load_main_view.js +++ /dev/null @@ -1,57 +0,0 @@ -import Vue from 'vue'; -import VueApollo from 'vue-apollo'; -import { parseBoolean } from '~/lib/utils/common_utils'; -import createDefaultClient from '~/lib/graphql'; -import ClustersMainView from './components/clusters_main_view.vue'; -import { createStore } from './store'; - -Vue.use(VueApollo); - -export default () => { - const el = document.querySelector('.js-clusters-main-view'); - - if (!el) { - return null; - } - - const defaultClient = createDefaultClient(); - - const { - emptyStateImage, - defaultBranchName, - projectPath, - kasAddress, - newClusterPath, - addClusterPath, - emptyStateHelpText, - clustersEmptyStateImage, - canAddCluster, - canAdminCluster, - gitlabVersion, - } = el.dataset; - - return new Vue({ - el, - apolloProvider: new VueApollo({ defaultClient }), - provide: { - emptyStateImage, - projectPath, - kasAddress, - newClusterPath, - addClusterPath, - emptyStateHelpText, - clustersEmptyStateImage, - canAddCluster: parseBoolean(canAddCluster), - canAdminCluster: parseBoolean(canAdminCluster), - gitlabVersion, - }, - store: createStore(el.dataset), - render(createElement) { - return createElement(ClustersMainView, { - props: { - defaultBranchName, - }, - }); - }, - }); -}; |