diff options
Diffstat (limited to 'app/assets/javascripts/pages/import/history')
4 files changed, 266 insertions, 0 deletions
diff --git a/app/assets/javascripts/pages/import/history/components/import_error_details.vue b/app/assets/javascripts/pages/import/history/components/import_error_details.vue new file mode 100644 index 00000000000..33ba73317f8 --- /dev/null +++ b/app/assets/javascripts/pages/import/history/components/import_error_details.vue @@ -0,0 +1,43 @@ +<script> +import { GlLoadingIcon } from '@gitlab/ui'; +import API from '~/api'; +import { createAlert } from '~/flash'; +import { DEFAULT_ERROR } from '../utils/error_messages'; + +export default { + components: { + GlLoadingIcon, + }, + props: { + id: { + type: Number, + required: true, + }, + }, + data() { + return { + loading: true, + error: null, + }; + }, + async mounted() { + try { + const { + data: { import_error: importError }, + } = await API.project(this.id); + this.error = importError; + } catch (e) { + createAlert({ message: DEFAULT_ERROR }); + this.error = null; + } finally { + this.loading = false; + } + }, +}; +</script> +<template> + <gl-loading-icon v-if="loading" size="md" /> + <pre + v-else + ><code>{{ error || s__('BulkImport|No additional information provided.') }}</code></pre> +</template> diff --git a/app/assets/javascripts/pages/import/history/components/import_history_app.vue b/app/assets/javascripts/pages/import/history/components/import_history_app.vue new file mode 100644 index 00000000000..557e25f66e2 --- /dev/null +++ b/app/assets/javascripts/pages/import/history/components/import_history_app.vue @@ -0,0 +1,199 @@ +<script> +import { GlButton, GlEmptyState, GlIcon, GlLink, GlLoadingIcon, GlTable } from '@gitlab/ui'; +import { s__, __ } from '~/locale'; +import createFlash from '~/flash'; +import { parseIntPagination, normalizeHeaders } from '~/lib/utils/common_utils'; +import { getProjects } from '~/rest_api'; +import ImportStatus from '~/import_entities/components/import_status.vue'; +import PaginationBar from '~/vue_shared/components/pagination_bar/pagination_bar.vue'; +import TimeAgo from '~/vue_shared/components/time_ago_tooltip.vue'; +import { DEFAULT_ERROR } from '../utils/error_messages'; +import ImportErrorDetails from './import_error_details.vue'; + +const DEFAULT_PER_PAGE = 20; +const DEFAULT_TH_CLASSES = + 'gl-bg-transparent! gl-border-b-solid! gl-border-b-gray-200! gl-border-b-1! gl-p-5!'; + +const tableCell = (config) => ({ + thClass: DEFAULT_TH_CLASSES, + tdClass: (value, key, item) => { + return { + // eslint-disable-next-line no-underscore-dangle + 'gl-border-b-0!': item._showDetails, + }; + }, + ...config, +}); + +export default { + components: { + GlButton, + GlEmptyState, + GlIcon, + GlLink, + GlLoadingIcon, + GlTable, + PaginationBar, + ImportStatus, + ImportErrorDetails, + TimeAgo, + }, + + inject: ['assets'], + + data() { + return { + loading: true, + historyItems: [], + paginationConfig: { + page: 1, + perPage: DEFAULT_PER_PAGE, + }, + pageInfo: {}, + }; + }, + + fields: [ + tableCell({ + key: 'source', + label: s__('BulkImport|Source'), + thClass: `${DEFAULT_TH_CLASSES} gl-w-30p`, + }), + tableCell({ + key: 'destination', + label: s__('BulkImport|Destination'), + thClass: `${DEFAULT_TH_CLASSES} gl-w-40p`, + }), + tableCell({ + key: 'created_at', + label: __('Date'), + }), + tableCell({ + key: 'status', + label: __('Status'), + tdAttr: { 'data-qa-selector': 'import_status_indicator' }, + }), + ], + + computed: { + hasHistoryItems() { + return this.historyItems.length > 0; + }, + }, + + watch: { + paginationConfig: { + handler() { + this.loadHistoryItems(); + }, + deep: true, + immediate: true, + }, + }, + + methods: { + async loadHistoryItems() { + try { + this.loading = true; + const { data: historyItems, headers } = await getProjects(undefined, { + imported: true, + simple: false, + page: this.paginationConfig.page, + per_page: this.paginationConfig.perPage, + }); + this.pageInfo = parseIntPagination(normalizeHeaders(headers)); + this.historyItems = historyItems; + } catch (e) { + createFlash({ message: DEFAULT_ERROR, captureError: true, error: e }); + } finally { + this.loading = false; + } + }, + + hasHttpProtocol(url) { + try { + const parsedUrl = new URL(url); + return ['http:', 'https:'].includes(parsedUrl.protocol); + } catch (e) { + return false; + } + }, + + setPageSize(size) { + this.paginationConfig.perPage = size; + this.paginationConfig.page = 1; + }, + }, +}; +</script> + +<template> + <div> + <div + class="gl-border-solid gl-border-gray-200 gl-border-0 gl-border-b-1 gl-display-flex gl-align-items-center" + > + <h1 class="gl-my-0 gl-py-4 gl-font-size-h1"> + <img :src="assets.gitlabLogo" class="gl-w-6 gl-h-6 gl-mb-2 gl-display-inline gl-mr-2" /> + {{ s__('BulkImport|Project import history') }} + </h1> + </div> + <gl-loading-icon v-if="loading" size="md" class="gl-mt-5" /> + <gl-empty-state + v-else-if="!hasHistoryItems" + :title="s__('BulkImport|No history is available')" + :description="s__('BulkImport|Your imported projects will appear here.')" + /> + <template v-else> + <gl-table + :fields="$options.fields" + :items="historyItems" + data-qa-selector="import_history_table" + class="gl-w-full" + > + <template #cell(source)="{ item }"> + <template v-if="item.import_url"> + <gl-link + v-if="hasHttpProtocol(item.import_url)" + :href="item.import_url" + target="_blank" + > + {{ item.import_url }} + <gl-icon name="external-link" class="gl-vertical-align-middle" /> + </gl-link> + <span v-else>{{ item.import_url }}</span> + </template> + <span v-else>{{ + s__('BulkImport|Template / File-based import / GitLab Migration') + }}</span> + </template> + <template #cell(destination)="{ item }"> + <gl-link :href="item.http_url_to_repo"> + {{ item.path_with_namespace }} + </gl-link> + </template> + <template #cell(created_at)="{ value }"> + <time-ago :time="value" /> + </template> + <template #cell(status)="{ item, toggleDetails, detailsShowing }"> + <import-status :status="item.import_status" class="gl-display-inline-block gl-w-13" /> + <gl-button + v-if="item.import_status === 'failed'" + class="gl-ml-3" + :selected="detailsShowing" + @click="toggleDetails" + >{{ __('Details') }}</gl-button + > + </template> + <template #row-details="{ item }"> + <import-error-details :id="item.id" /> + </template> + </gl-table> + <pagination-bar + :page-info="pageInfo" + class="gl-m-0 gl-mt-3" + @set-page="paginationConfig.page = $event" + @set-page-size="setPageSize" + /> + </template> + </div> +</template> diff --git a/app/assets/javascripts/pages/import/history/index.js b/app/assets/javascripts/pages/import/history/index.js new file mode 100644 index 00000000000..d540272c266 --- /dev/null +++ b/app/assets/javascripts/pages/import/history/index.js @@ -0,0 +1,21 @@ +import Vue from 'vue'; +import ImportHistoryApp from './components/import_history_app.vue'; + +function mountImportHistoryApp(mountElement) { + if (!mountElement) return undefined; + + return new Vue({ + el: mountElement, + name: 'ImportHistoryRoot', + provide: { + assets: { + gitlabLogo: mountElement.dataset.logo, + }, + }, + render(createElement) { + return createElement(ImportHistoryApp); + }, + }); +} + +mountImportHistoryApp(document.querySelector('#import-history-mount-element')); diff --git a/app/assets/javascripts/pages/import/history/utils/error_messages.js b/app/assets/javascripts/pages/import/history/utils/error_messages.js new file mode 100644 index 00000000000..24669e22ade --- /dev/null +++ b/app/assets/javascripts/pages/import/history/utils/error_messages.js @@ -0,0 +1,3 @@ +import { __ } from '~/locale'; + +export const DEFAULT_ERROR = __('Something went wrong on our end.'); |