diff options
Diffstat (limited to 'app')
-rw-r--r-- | app/assets/javascripts/admin/deploy_keys/components/table.vue | 152 | ||||
-rw-r--r-- | app/assets/javascripts/api.js | 7 | ||||
-rw-r--r-- | app/assets/javascripts/notes/components/noteable_note.vue | 2 |
3 files changed, 157 insertions, 4 deletions
diff --git a/app/assets/javascripts/admin/deploy_keys/components/table.vue b/app/assets/javascripts/admin/deploy_keys/components/table.vue index 97a5a2f2f32..e971a1d1550 100644 --- a/app/assets/javascripts/admin/deploy_keys/components/table.vue +++ b/app/assets/javascripts/admin/deploy_keys/components/table.vue @@ -1,13 +1,28 @@ <script> -import { GlTable, GlButton } from '@gitlab/ui'; +import { GlTable, GlButton, GlPagination, GlLoadingIcon, GlEmptyState } from '@gitlab/ui'; import { __ } from '~/locale'; +import Api, { DEFAULT_PER_PAGE } from '~/api'; +import TimeAgoTooltip from '~/vue_shared/components/time_ago_tooltip.vue'; +import { cleanLeadingSeparator } from '~/lib/utils/url_utility'; +import createFlash from '~/flash'; export default { name: 'DeployKeysTable', i18n: { pageTitle: __('Public deploy keys'), newDeployKeyButtonText: __('New deploy key'), + emptyStateTitle: __('No public deploy keys'), + emptyStateDescription: __( + 'Deploy keys grant read/write access to all repositories in your instance', + ), + remove: __('Remove deploy key'), + edit: __('Edit deploy key'), + pagination: { + next: __('Next'), + prev: __('Prev'), + }, + apiErrorMessage: __('An error occurred fetching the public deploy keys. Please try again.'), }, fields: [ { @@ -29,13 +44,83 @@ export default { { key: 'actions', label: __('Actions'), + tdClass: 'gl-lg-w-1px gl-white-space-nowrap', + thClass: 'gl-lg-w-1px gl-white-space-nowrap', }, ], + DEFAULT_PER_PAGE, components: { GlTable, GlButton, + GlPagination, + TimeAgoTooltip, + GlLoadingIcon, + GlEmptyState, }, inject: ['editPath', 'deletePath', 'createPath', 'emptyStateSvgPath'], + data() { + return { + page: 1, + totalItems: 0, + loading: false, + items: [], + }; + }, + computed: { + shouldShowTable() { + return this.totalItems !== 0 || this.loading; + }, + }, + watch: { + page(newPage) { + this.fetchDeployKeys(newPage); + }, + }, + mounted() { + this.fetchDeployKeys(); + }, + methods: { + editHref(id) { + return this.editPath.replace(':id', id); + }, + projectHref(project) { + return `/${cleanLeadingSeparator(project.path_with_namespace)}`; + }, + async fetchDeployKeys(page) { + this.loading = true; + try { + const { headers, data: items } = await Api.deployKeys({ + page, + public: true, + }); + + if (this.totalItems === 0) { + this.totalItems = parseInt(headers?.['x-total'], 10) || 0; + } + + this.items = items.map( + ({ id, title, fingerprint, projects_with_write_access, created_at }) => ({ + id, + title, + fingerprint, + projects: projects_with_write_access, + created: created_at, + }), + ); + } catch (error) { + createFlash({ + message: this.$options.i18n.apiErrorMessage, + captureError: true, + error, + }); + + this.totalItems = 0; + + this.items = []; + } + this.loading = false; + }, + }, }; </script> @@ -45,10 +130,71 @@ export default { <h4 class="gl-m-0"> {{ $options.i18n.pageTitle }} </h4> - <gl-button variant="confirm" :href="createPath">{{ + <gl-button variant="confirm" :href="createPath" data-testid="new-deploy-key-button">{{ $options.i18n.newDeployKeyButtonText }}</gl-button> </div> - <gl-table :fields="$options.fields" data-testid="deploy-keys-list" /> + <template v-if="shouldShowTable"> + <gl-table + :busy="loading" + :items="items" + :fields="$options.fields" + stacked="lg" + data-testid="deploy-keys-list" + > + <template #table-busy> + <gl-loading-icon size="lg" class="gl-my-5" /> + </template> + + <template #cell(projects)="{ item: { projects } }"> + <a + v-for="project in projects" + :key="project.id" + :href="projectHref(project)" + class="gl-display-block" + >{{ project.name_with_namespace }}</a + > + </template> + + <template #cell(fingerprint)="{ item: { fingerprint } }"> + <code>{{ fingerprint }}</code> + </template> + + <template #cell(created)="{ item: { created } }"> + <time-ago-tooltip :time="created" /> + </template> + + <template #head(actions)="{ label }"> + <span class="gl-sr-only">{{ label }}</span> + </template> + + <template #cell(actions)="{ item: { id } }"> + <gl-button + icon="pencil" + :aria-label="$options.i18n.edit" + :href="editHref(id)" + class="gl-mr-2" + /> + <gl-button variant="danger" icon="remove" :aria-label="$options.i18n.remove" /> + </template> + </gl-table> + <gl-pagination + v-if="!loading" + v-model="page" + :per-page="$options.DEFAULT_PER_PAGE" + :total-items="totalItems" + :next-text="$options.i18n.pagination.next" + :prev-text="$options.i18n.pagination.prev" + align="center" + /> + </template> + <gl-empty-state + v-else + :svg-path="emptyStateSvgPath" + :title="$options.i18n.emptyStateTitle" + :description="$options.i18n.emptyStateDescription" + :primary-button-text="$options.i18n.newDeployKeyButtonText" + :primary-button-link="createPath" + /> </div> </template> diff --git a/app/assets/javascripts/api.js b/app/assets/javascripts/api.js index adf3e122a64..8c996b448aa 100644 --- a/app/assets/javascripts/api.js +++ b/app/assets/javascripts/api.js @@ -91,6 +91,7 @@ const Api = { projectNotificationSettingsPath: '/api/:version/projects/:id/notification_settings', groupNotificationSettingsPath: '/api/:version/groups/:id/notification_settings', notificationSettingsPath: '/api/:version/notification_settings', + deployKeysPath: '/api/:version/deploy_keys', group(groupId, callback = () => {}) { const url = Api.buildUrl(Api.groupPath).replace(':id', groupId); @@ -950,6 +951,12 @@ const Api = { return axios.delete(url); }, + deployKeys(params = {}) { + const url = Api.buildUrl(this.deployKeysPath); + + return axios.get(url, { params: { per_page: DEFAULT_PER_PAGE, ...params } }); + }, + async updateNotificationSettings(projectId, groupId, data = {}) { let url = Api.buildUrl(this.notificationSettingsPath); diff --git a/app/assets/javascripts/notes/components/noteable_note.vue b/app/assets/javascripts/notes/components/noteable_note.vue index e35d8d94289..e74d4deeaec 100644 --- a/app/assets/javascripts/notes/components/noteable_note.vue +++ b/app/assets/javascripts/notes/components/noteable_note.vue @@ -388,7 +388,7 @@ export default { <div v-if="showMultiLineComment" data-testid="multiline-comment" - class="gl-mb-3 gl-text-gray-500 gl-border-gray-200 gl-border-b-solid gl-border-b-1 gl-pb-3" + class="gl-mb-5 gl-text-gray-500 gl-border-gray-100 gl-border-b-solid gl-border-b-1 gl-pb-4" > <gl-sprintf :message="__('Comment on lines %{startLine} to %{endLine}')"> <template #startLine> |