234 lines
6.6 KiB
Vue
234 lines
6.6 KiB
Vue
<script>
|
|
import { GlEmptyState, GlSprintf, GlLink, GlSkeletonLoader } from '@gitlab/ui';
|
|
import HarborListHeader from '~/packages_and_registries/harbor_registry/components/list/harbor_list_header.vue';
|
|
import PersistedSearch from '~/packages_and_registries/shared/components/persisted_search.vue';
|
|
import HarborList from '~/packages_and_registries/harbor_registry/components/list/harbor_list.vue';
|
|
import { convertObjectPropsToCamelCase } from '~/lib/utils/common_utils';
|
|
import {
|
|
extractSortingDetail,
|
|
formatPagination,
|
|
parseFilter,
|
|
dockerBuildCommand,
|
|
dockerPushCommand,
|
|
dockerLoginCommand,
|
|
} from '~/packages_and_registries/harbor_registry/utils';
|
|
import { createAlert } from '~/flash';
|
|
import {
|
|
SORT_FIELDS,
|
|
CONNECTION_ERROR_TITLE,
|
|
CONNECTION_ERROR_MESSAGE,
|
|
EMPTY_RESULT_TITLE,
|
|
EMPTY_RESULT_MESSAGE,
|
|
DEFAULT_PER_PAGE,
|
|
FETCH_IMAGES_LIST_ERROR_MESSAGE,
|
|
EMPTY_IMAGES_TITLE,
|
|
EMPTY_IMAGES_MESSAGE,
|
|
HARBOR_REGISTRY_HELP_PAGE_PATH,
|
|
} from '~/packages_and_registries/harbor_registry/constants';
|
|
import Tracking from '~/tracking';
|
|
import { getHarborRepositoriesList } from '~/rest_api';
|
|
|
|
export default {
|
|
name: 'HarborListPage',
|
|
components: {
|
|
HarborListHeader,
|
|
HarborList,
|
|
GlSkeletonLoader,
|
|
GlEmptyState,
|
|
GlSprintf,
|
|
GlLink,
|
|
PersistedSearch,
|
|
CliCommands: () =>
|
|
import(
|
|
/* webpackChunkName: 'harbor_registry_components' */ '~/packages_and_registries/shared/components/cli_commands.vue'
|
|
),
|
|
},
|
|
mixins: [Tracking.mixin()],
|
|
inject: [
|
|
'endpoint',
|
|
'repositoryUrl',
|
|
'harborIntegrationProjectName',
|
|
'projectName',
|
|
'isGroupPage',
|
|
'connectionError',
|
|
'invalidPathError',
|
|
'containersErrorImage',
|
|
'noContainersImage',
|
|
],
|
|
loader: {
|
|
repeat: 10,
|
|
width: 1000,
|
|
height: 40,
|
|
},
|
|
i18n: {
|
|
connectionErrorTitle: CONNECTION_ERROR_TITLE,
|
|
connectionErrorMessage: CONNECTION_ERROR_MESSAGE,
|
|
},
|
|
searchConfig: SORT_FIELDS,
|
|
helpPagePath: HARBOR_REGISTRY_HELP_PAGE_PATH,
|
|
data() {
|
|
return {
|
|
images: [],
|
|
totalCount: 0,
|
|
pageInfo: {},
|
|
filter: [],
|
|
isLoading: true,
|
|
sorting: null,
|
|
name: null,
|
|
};
|
|
},
|
|
computed: {
|
|
dockerCommand() {
|
|
return {
|
|
build: dockerBuildCommand({
|
|
repositoryUrl: this.repositoryUrl,
|
|
harborProjectName: this.harborIntegrationProjectName,
|
|
projectName: this.projectName,
|
|
}),
|
|
push: dockerPushCommand({
|
|
repositoryUrl: this.repositoryUrl,
|
|
harborProjectName: this.harborIntegrationProjectName,
|
|
projectName: this.projectName,
|
|
}),
|
|
login: dockerLoginCommand(this.repositoryUrl),
|
|
};
|
|
},
|
|
showCommands() {
|
|
return !this.isLoading && !this.isGroupPage && this.images?.length;
|
|
},
|
|
showConnectionError() {
|
|
return this.connectionError || this.invalidPathError;
|
|
},
|
|
currentPage() {
|
|
return this.pageInfo.page || 1;
|
|
},
|
|
emptyStateTexts() {
|
|
return {
|
|
title: this.name ? EMPTY_RESULT_TITLE : EMPTY_IMAGES_TITLE,
|
|
message: this.name ? EMPTY_RESULT_MESSAGE : EMPTY_IMAGES_MESSAGE,
|
|
};
|
|
},
|
|
},
|
|
methods: {
|
|
fetchHarborImages(requestPage) {
|
|
this.isLoading = true;
|
|
|
|
const { orderBy, sort } = extractSortingDetail(this.sorting);
|
|
const sortOptions = `${orderBy} ${sort}`;
|
|
|
|
const params = {
|
|
requestPath: this.endpoint,
|
|
limit: DEFAULT_PER_PAGE,
|
|
search: this.name,
|
|
page: requestPage,
|
|
sort: sortOptions,
|
|
};
|
|
|
|
getHarborRepositoriesList(params)
|
|
.then((res) => {
|
|
this.images = (res?.data || []).map((item) => {
|
|
return convertObjectPropsToCamelCase(item);
|
|
});
|
|
const pagination = formatPagination(res.headers);
|
|
|
|
this.totalCount = pagination?.total || 0;
|
|
this.pageInfo = pagination;
|
|
|
|
this.isLoading = false;
|
|
})
|
|
.catch(() => {
|
|
createAlert({ message: FETCH_IMAGES_LIST_ERROR_MESSAGE });
|
|
});
|
|
},
|
|
handleSearchUpdate({ sort, filters }) {
|
|
this.sorting = sort;
|
|
this.name = parseFilter(filters, 'name');
|
|
|
|
this.fetchHarborImages(1);
|
|
},
|
|
fetchPrevPage() {
|
|
const prevPageNum = this.currentPage - 1;
|
|
this.fetchHarborImages(prevPageNum);
|
|
},
|
|
fetchNextPage() {
|
|
const nextPageNum = this.currentPage + 1;
|
|
this.fetchHarborImages(nextPageNum);
|
|
},
|
|
},
|
|
};
|
|
</script>
|
|
|
|
<template>
|
|
<div>
|
|
<gl-empty-state
|
|
v-if="showConnectionError"
|
|
:title="$options.i18n.connectionErrorTitle"
|
|
:svg-path="containersErrorImage"
|
|
>
|
|
<template #description>
|
|
<p>
|
|
<gl-sprintf :message="$options.i18n.connectionErrorMessage">
|
|
<template #docLink="{ content }">
|
|
<gl-link :href="$options.helpPagePath" target="_blank">
|
|
{{ content }}
|
|
</gl-link>
|
|
</template>
|
|
</gl-sprintf>
|
|
</p>
|
|
</template>
|
|
</gl-empty-state>
|
|
<template v-else>
|
|
<harbor-list-header :metadata-loading="isLoading" :images-count="totalCount">
|
|
<template #commands>
|
|
<cli-commands
|
|
v-if="showCommands"
|
|
:docker-build-command="dockerCommand.build"
|
|
:docker-push-command="dockerCommand.push"
|
|
:docker-login-command="dockerCommand.login"
|
|
/>
|
|
</template>
|
|
</harbor-list-header>
|
|
<persisted-search
|
|
:sortable-fields="$options.searchConfig"
|
|
:default-order="$options.searchConfig[0].orderBy"
|
|
default-sort="desc"
|
|
@update="handleSearchUpdate"
|
|
/>
|
|
|
|
<div v-if="isLoading" class="gl-mt-5">
|
|
<gl-skeleton-loader
|
|
v-for="index in $options.loader.repeat"
|
|
:key="index"
|
|
:width="$options.loader.width"
|
|
:height="$options.loader.height"
|
|
preserve-aspect-ratio="xMinYMax meet"
|
|
>
|
|
<rect width="500" x="10" y="10" height="20" rx="4" />
|
|
<circle cx="525" cy="20" r="10" />
|
|
<rect x="960" y="0" width="40" height="40" rx="4" />
|
|
</gl-skeleton-loader>
|
|
</div>
|
|
<template v-else>
|
|
<harbor-list
|
|
v-if="images.length"
|
|
:images="images"
|
|
:metadata-loading="isLoading"
|
|
:page-info="pageInfo"
|
|
@prev-page="fetchPrevPage"
|
|
@next-page="fetchNextPage"
|
|
/>
|
|
<gl-empty-state
|
|
v-else
|
|
:svg-path="noContainersImage"
|
|
data-testid="emptySearch"
|
|
:title="emptyStateTexts.title"
|
|
>
|
|
<template #description>
|
|
{{ emptyStateTexts.message }}
|
|
</template>
|
|
</gl-empty-state>
|
|
</template>
|
|
</template>
|
|
</div>
|
|
</template>
|