gitlab-ce/app/assets/javascripts/organizations/shared/components/projects_view.vue

214 lines
5.5 KiB
Vue

<script>
import { GlLoadingIcon, GlKeysetPagination } from '@gitlab/ui';
import { s__, __ } from '~/locale';
import ProjectsList from '~/vue_shared/components/projects_list/projects_list.vue';
import { ACTION_DELETE } from '~/vue_shared/components/list_actions/constants';
import { DEFAULT_PER_PAGE } from '~/api';
import { deleteProject } from '~/rest_api';
import { createAlert } from '~/alert';
import {
renderDeleteSuccessToast,
deleteParams,
formatProjects,
timestampType,
} from 'ee_else_ce/organizations/shared/utils';
import { SORT_ITEM_NAME, SORT_DIRECTION_ASC } from '../constants';
import projectsQuery from '../graphql/queries/projects.query.graphql';
import NewProjectButton from './new_project_button.vue';
import GroupsAndProjectsEmptyState from './groups_and_projects_empty_state.vue';
export default {
i18n: {
errorMessage: s__(
'Organization|An error occurred loading the projects. Please refresh the page to try again.',
),
deleteErrorMessage: s__(
'Organization|An error occurred deleting the project. Please refresh the page to try again.',
),
emptyState: {
title: s__("Organization|You don't have any projects yet."),
description: s__(
'GroupsEmptyState|Projects are where you can store your code, access issues, wiki, and other features of GitLab.',
),
},
project: __('Project'),
},
components: {
ProjectsList,
GlLoadingIcon,
GlKeysetPagination,
NewProjectButton,
GroupsAndProjectsEmptyState,
},
inject: {
organizationGid: {},
projectsEmptyStateSvgPath: {},
},
props: {
shouldShowEmptyStateButtons: {
type: Boolean,
required: false,
default: false,
},
listItemClass: {
type: [String, Array, Object],
required: false,
default: '',
},
startCursor: {
type: String,
required: false,
default: null,
},
endCursor: {
type: String,
required: false,
default: null,
},
perPage: {
type: Number,
required: false,
default: DEFAULT_PER_PAGE,
},
search: {
type: String,
required: false,
default: '',
},
sortName: {
type: String,
required: false,
default: SORT_ITEM_NAME.value,
},
sortDirection: {
type: String,
required: false,
default: SORT_DIRECTION_ASC,
},
},
data() {
return {
projects: {},
};
},
apollo: {
projects: {
query: projectsQuery,
variables() {
return {
id: this.organizationGid,
search: this.search,
sort: this.sort,
...this.pagination,
};
},
update({
organization: {
projects: { nodes, pageInfo },
},
}) {
return {
nodes: formatProjects(nodes),
pageInfo,
};
},
error(error) {
createAlert({ message: this.$options.i18n.errorMessage, error, captureError: true });
},
},
},
computed: {
nodes() {
return this.projects.nodes || [];
},
pageInfo() {
return this.projects.pageInfo || {};
},
pagination() {
if (!this.startCursor && !this.endCursor) {
return {
first: this.perPage,
after: null,
last: null,
before: null,
};
}
return {
first: this.endCursor && this.perPage,
after: this.endCursor,
last: this.startCursor && this.perPage,
before: this.startCursor,
};
},
sort() {
return `${this.sortName}_${this.sortDirection}`;
},
isLoading() {
return this.$apollo.queries.projects.loading;
},
timestampType() {
return timestampType(this.sortName);
},
},
methods: {
onNext(endCursor) {
this.$emit('page-change', {
endCursor,
startCursor: null,
});
},
onPrev(startCursor) {
this.$emit('page-change', {
endCursor: null,
startCursor,
});
},
setProjectIsDeleting(nodeIndex, value) {
this.projects.nodes[nodeIndex].actionLoadingStates[ACTION_DELETE] = value;
},
async deleteProject(project) {
const nodeIndex = this.projects.nodes.findIndex((node) => node.id === project.id);
try {
this.setProjectIsDeleting(nodeIndex, true);
await deleteProject(project.id, deleteParams(project));
this.$apollo.queries.projects.refetch();
renderDeleteSuccessToast(project, this.$options.i18n.project);
} catch (error) {
createAlert({ message: this.$options.i18n.deleteErrorMessage, error, captureError: true });
} finally {
this.setProjectIsDeleting(nodeIndex, false);
}
},
},
};
</script>
<template>
<gl-loading-icon v-if="isLoading" class="gl-mt-5" size="md" />
<div v-else-if="nodes.length">
<projects-list
:projects="nodes"
show-project-icon
:list-item-class="listItemClass"
:timestamp-type="timestampType"
@delete="deleteProject"
/>
<div v-if="pageInfo.hasNextPage || pageInfo.hasPreviousPage" class="gl-text-center gl-mt-5">
<gl-keyset-pagination v-bind="pageInfo" @prev="onPrev" @next="onNext" />
</div>
</div>
<groups-and-projects-empty-state
v-else
:svg-path="projectsEmptyStateSvgPath"
:title="$options.i18n.emptyState.title"
:description="$options.i18n.emptyState.description"
:search="search"
>
<template v-if="shouldShowEmptyStateButtons" #actions>
<new-project-button />
</template>
</groups-and-projects-empty-state>
</template>