diff options
Diffstat (limited to 'app/assets/javascripts/clusters_list/components')
3 files changed, 248 insertions, 23 deletions
diff --git a/app/assets/javascripts/clusters_list/components/agent_options.vue b/app/assets/javascripts/clusters_list/components/agent_options.vue new file mode 100644 index 00000000000..a364122ba56 --- /dev/null +++ b/app/assets/javascripts/clusters_list/components/agent_options.vue @@ -0,0 +1,200 @@ +<script> +import { + GlDropdown, + GlDropdownItem, + GlModal, + GlModalDirective, + GlSprintf, + GlFormGroup, + GlFormInput, +} from '@gitlab/ui'; +import { s__, __, sprintf } from '~/locale'; +import { DELETE_AGENT_MODAL_ID } from '../constants'; +import deleteAgent from '../graphql/mutations/delete_agent.mutation.graphql'; +import getAgentsQuery from '../graphql/queries/get_agents.query.graphql'; +import { removeAgentFromStore } from '../graphql/cache_update'; + +export default { + i18n: { + dropdownText: __('More options'), + deleteButton: s__('ClusterAgents|Delete agent'), + modalTitle: __('Are you sure?'), + modalBody: s__( + 'ClusterAgents|Are you sure you want to delete this agent? You cannot undo this.', + ), + modalInputLabel: s__('ClusterAgents|To delete the agent, type %{name} to confirm:'), + modalAction: s__('ClusterAgents|Delete'), + modalCancel: __('Cancel'), + successMessage: s__('ClusterAgents|%{name} successfully deleted'), + defaultError: __('An error occurred. Please try again.'), + }, + components: { + GlDropdown, + GlDropdownItem, + GlModal, + GlSprintf, + GlFormGroup, + GlFormInput, + }, + directives: { + GlModalDirective, + }, + inject: ['projectPath'], + props: { + agent: { + required: true, + type: Object, + validator: (value) => ['id', 'name'].every((prop) => value[prop]), + }, + defaultBranchName: { + default: '.noBranch', + required: false, + type: String, + }, + maxAgents: { + default: null, + required: false, + type: Number, + }, + }, + data() { + return { + loading: false, + error: null, + deleteConfirmText: null, + agentName: this.agent.name, + }; + }, + computed: { + getAgentsQueryVariables() { + return { + defaultBranchName: this.defaultBranchName, + first: this.maxAgents, + last: null, + projectPath: this.projectPath, + }; + }, + modalId() { + return sprintf(DELETE_AGENT_MODAL_ID, { + agentName: this.agent.name, + }); + }, + primaryModalProps() { + return { + text: this.$options.i18n.modalAction, + attributes: [ + { disabled: this.loading || this.disableModalSubmit, loading: this.loading }, + { variant: 'danger' }, + ], + }; + }, + cancelModalProps() { + return { + text: this.$options.i18n.modalCancel, + attributes: [], + }; + }, + disableModalSubmit() { + return this.deleteConfirmText !== this.agent.name; + }, + }, + methods: { + async deleteAgent() { + if (this.disableModalSubmit || this.loading) { + return; + } + + this.loading = true; + this.error = null; + + try { + const { errors } = await this.deleteAgentMutation(); + + if (errors.length) { + throw new Error(errors[0]); + } + } catch (error) { + this.error = error?.message || this.$options.i18n.defaultError; + } finally { + this.loading = false; + const successMessage = sprintf(this.$options.i18n.successMessage, { name: this.agentName }); + + this.$toast.show(this.error || successMessage); + + this.$refs.modal.hide(); + } + }, + deleteAgentMutation() { + return this.$apollo + .mutate({ + mutation: deleteAgent, + variables: { + input: { + id: this.agent.id, + }, + }, + update: (store) => { + const deleteClusterAgent = this.agent; + removeAgentFromStore( + store, + deleteClusterAgent, + getAgentsQuery, + this.getAgentsQueryVariables, + ); + }, + }) + + .then(({ data: { clusterAgentDelete } }) => { + return clusterAgentDelete; + }); + }, + hideModal() { + this.loading = false; + this.error = null; + this.deleteConfirmText = null; + }, + }, +}; +</script> + +<template> + <div> + <gl-dropdown + icon="ellipsis_v" + right + :disabled="loading" + :text="$options.i18n.dropdownText" + text-sr-only + category="tertiary" + no-caret + > + <gl-dropdown-item v-gl-modal-directive="modalId"> + {{ $options.i18n.deleteButton }} + </gl-dropdown-item> + </gl-dropdown> + + <gl-modal + ref="modal" + :modal-id="modalId" + :title="$options.i18n.modalTitle" + :action-primary="primaryModalProps" + :action-cancel="cancelModalProps" + size="sm" + @primary="deleteAgent" + @hide="hideModal" + > + <p>{{ $options.i18n.modalBody }}</p> + + <gl-form-group> + <template #label> + <gl-sprintf :message="$options.i18n.modalInputLabel"> + <template #name> + <code>{{ agent.name }}</code> + </template> + </gl-sprintf> + </template> + <gl-form-input v-model="deleteConfirmText" @keydown.enter="deleteAgent" /> + </gl-form-group> + </gl-modal> + </div> +</template> diff --git a/app/assets/javascripts/clusters_list/components/agent_table.vue b/app/assets/javascripts/clusters_list/components/agent_table.vue index 000730ac1ba..695e16b7b4b 100644 --- a/app/assets/javascripts/clusters_list/components/agent_table.vue +++ b/app/assets/javascripts/clusters_list/components/agent_table.vue @@ -1,21 +1,23 @@ <script> -import { - GlLink, - GlModalDirective, - GlTable, - GlIcon, - GlSprintf, - GlTooltip, - GlPopover, -} from '@gitlab/ui'; -import { s__ } from '~/locale'; +import { GlLink, GlTable, GlIcon, GlSprintf, GlTooltip, GlPopover } from '@gitlab/ui'; +import { s__, __ } from '~/locale'; import TimeAgoTooltip from '~/vue_shared/components/time_ago_tooltip.vue'; import timeagoMixin from '~/vue_shared/mixins/timeago'; import { helpPagePath } from '~/helpers/help_page_helper'; -import { INSTALL_AGENT_MODAL_ID, AGENT_STATUSES } from '../constants'; +import { AGENT_STATUSES } from '../constants'; import { getAgentConfigPath } from '../clusters_util'; +import AgentOptions from './agent_options.vue'; export default { + i18n: { + nameLabel: s__('ClusterAgents|Name'), + statusLabel: s__('ClusterAgents|Connection status'), + lastContactLabel: s__('ClusterAgents|Last contact'), + configurationLabel: s__('ClusterAgents|Configuration'), + optionsLabel: __('Options'), + troubleshootingText: s__('ClusterAgents|Learn how to troubleshoot'), + neverConnectedText: s__('ClusterAgents|Never'), + }, components: { GlLink, GlTable, @@ -24,14 +26,10 @@ export default { GlTooltip, GlPopover, TimeAgoTooltip, - }, - directives: { - GlModalDirective, + AgentOptions, }, mixins: [timeagoMixin], - INSTALL_AGENT_MODAL_ID, AGENT_STATUSES, - troubleshooting_link: helpPagePath('user/clusters/agent/index', { anchor: 'troubleshooting', }), @@ -40,6 +38,16 @@ export default { required: true, type: Array, }, + defaultBranchName: { + default: '.noBranch', + required: false, + type: String, + }, + maxAgents: { + default: null, + required: false, + type: Number, + }, }, computed: { fields() { @@ -47,22 +55,27 @@ export default { return [ { key: 'name', - label: s__('ClusterAgents|Name'), + label: this.$options.i18n.nameLabel, tdClass, }, { key: 'status', - label: s__('ClusterAgents|Connection status'), + label: this.$options.i18n.statusLabel, tdClass, }, { key: 'lastContact', - label: s__('ClusterAgents|Last contact'), + label: this.$options.i18n.lastContactLabel, tdClass, }, { key: 'configuration', - label: s__('ClusterAgents|Configuration'), + label: this.$options.i18n.configurationLabel, + tdClass, + }, + { + key: 'options', + label: this.$options.i18n.optionsLabel, tdClass, }, ]; @@ -118,7 +131,7 @@ export default { </p> <p class="gl-mb-0"> <gl-link :href="$options.troubleshooting_link" target="_blank" class="gl-font-sm"> - {{ s__('ClusterAgents|Learn how to troubleshoot') }}</gl-link + {{ $options.i18n.troubleshootingText }}</gl-link > </p> </gl-popover> @@ -127,7 +140,7 @@ export default { <template #cell(lastContact)="{ item }"> <span data-testid="cluster-agent-last-contact"> <time-ago-tooltip v-if="item.lastContact" :time="item.lastContact" /> - <span v-else>{{ s__('ClusterAgents|Never') }}</span> + <span v-else>{{ $options.i18n.neverConnectedText }}</span> </span> </template> @@ -140,5 +153,13 @@ export default { <span v-else>{{ getAgentConfigPath(item.name) }}</span> </span> </template> + + <template #cell(options)="{ item }"> + <agent-options + :agent="item" + :default-branch-name="defaultBranchName" + :max-agents="maxAgents" + /> + </template> </gl-table> </template> diff --git a/app/assets/javascripts/clusters_list/components/agents.vue b/app/assets/javascripts/clusters_list/components/agents.vue index 45108a28e37..4fc421e7c31 100644 --- a/app/assets/javascripts/clusters_list/components/agents.vue +++ b/app/assets/javascripts/clusters_list/components/agents.vue @@ -151,7 +151,11 @@ export default { <section v-else-if="agentList"> <div v-if="agentList.length"> - <agent-table :agents="agentList" /> + <agent-table + :agents="agentList" + :default-branch-name="defaultBranchName" + :max-agents="cursor.first" + /> <div v-if="showPagination" class="gl-display-flex gl-justify-content-center gl-mt-5"> <gl-keyset-pagination v-bind="agentPageInfo" @prev="prevPage" @next="nextPage" /> |