diff options
Diffstat (limited to 'app/assets/javascripts/groups')
8 files changed, 160 insertions, 55 deletions
diff --git a/app/assets/javascripts/groups/components/app.vue b/app/assets/javascripts/groups/components/app.vue index ebfffdaaf50..c6fe16b13b5 100644 --- a/app/assets/javascripts/groups/components/app.vue +++ b/app/assets/javascripts/groups/components/app.vue @@ -114,9 +114,9 @@ export default { showModal() { this.isModalVisible = true; }, - fetchGroups({ parentId, page, filterGroupsBy, sortBy, archived, updatePagination }) { + fetchGroups({ parentId, page, filterGroupsBy, sortBy, updatePagination }) { return this.service - .getGroups(parentId, page, filterGroupsBy, sortBy, archived) + .getGroups(parentId, page, filterGroupsBy, sortBy) .then((res) => { if (updatePagination) { this.updatePagination(res.headers); @@ -133,7 +133,6 @@ export default { fetchAllGroups() { const page = getParameterByName('page') || null; const sortBy = getParameterByName('sort') || null; - const archived = getParameterByName('archived') || null; this.isLoading = true; @@ -141,7 +140,6 @@ export default { page, filterGroupsBy: this.filterGroupsBy, sortBy, - archived, updatePagination: true, }).then((res) => { this.isLoading = false; @@ -160,14 +158,13 @@ export default { this.updateGroups(res, Boolean(filterGroupsBy)); }); }, - fetchPage({ page, filterGroupsBy, sortBy, archived }) { + fetchPage({ page, filterGroupsBy, sortBy }) { this.isLoading = true; return this.fetchGroups({ page, filterGroupsBy, sortBy, - archived, updatePagination: true, }).then((res) => { this.isLoading = false; diff --git a/app/assets/javascripts/groups/components/item_stats.vue b/app/assets/javascripts/groups/components/item_stats.vue index 5674e28f5da..d87190edfd2 100644 --- a/app/assets/javascripts/groups/components/item_stats.vue +++ b/app/assets/javascripts/groups/components/item_stats.vue @@ -36,6 +36,9 @@ export default { <template> <div class="stats gl-text-gray-500"> + <div v-if="isProjectPendingRemoval"> + <gl-badge class="gl-mr-2" variant="warning">{{ __('pending deletion') }}</gl-badge> + </div> <item-stats-value v-if="displayValue(item.subgroupCount)" :title="__('Subgroups')" @@ -65,9 +68,6 @@ export default { css-class="project-stars" icon-name="star" /> - <div v-if="isProjectPendingRemoval"> - <gl-badge variant="warning">{{ __('pending deletion') }}</gl-badge> - </div> <div v-if="isProject" class="last-updated"> <time-ago-tooltip :time="item.lastActivityAt" tooltip-placement="bottom" /> </div> diff --git a/app/assets/javascripts/groups/components/overview_tabs.vue b/app/assets/javascripts/groups/components/overview_tabs.vue index 982dab45117..90a0582cc9f 100644 --- a/app/assets/javascripts/groups/components/overview_tabs.vue +++ b/app/assets/javascripts/groups/components/overview_tabs.vue @@ -3,13 +3,17 @@ import { GlTabs, GlTab, GlSearchBoxByType, GlSorting, GlSortingItem } from '@git import { isString, debounce } from 'lodash'; import { __ } from '~/locale'; import { DEBOUNCE_DELAY } from '~/vue_shared/components/filtered_search_bar/constants'; +import { markRaw } from '~/lib/utils/vue3compat/mark_raw'; import GroupsStore from '../store/groups_store'; import GroupsService from '../service/groups_service'; +import ArchivedProjectsService from '../service/archived_projects_service'; import { ACTIVE_TAB_SUBGROUPS_AND_PROJECTS, ACTIVE_TAB_SHARED, ACTIVE_TAB_ARCHIVED, + SORTING_ITEM_NAME, OVERVIEW_TABS_SORTING_ITEMS, + OVERVIEW_TABS_ARCHIVED_PROJECTS_SORTING_ITEMS, } from '../constants'; import eventHub from '../event_hub'; import GroupsApp from './app.vue'; @@ -17,7 +21,6 @@ import SubgroupsAndProjectsEmptyState from './empty_states/subgroups_and_project import SharedProjectsEmptyState from './empty_states/shared_projects_empty_state.vue'; import ArchivedProjectsEmptyState from './empty_states/archived_projects_empty_state.vue'; -const [SORTING_ITEM_NAME] = OVERVIEW_TABS_SORTING_ITEMS; const MIN_SEARCH_LENGTH = 3; export default { @@ -32,32 +35,38 @@ export default { SharedProjectsEmptyState, ArchivedProjectsEmptyState, }, - inject: ['endpoints', 'initialSort'], + inject: ['endpoints', 'initialSort', 'groupId'], data() { const tabs = [ { title: this.$options.i18n[ACTIVE_TAB_SUBGROUPS_AND_PROJECTS], key: ACTIVE_TAB_SUBGROUPS_AND_PROJECTS, - emptyStateComponent: SubgroupsAndProjectsEmptyState, + emptyStateComponent: markRaw(SubgroupsAndProjectsEmptyState), lazy: this.$route.name !== ACTIVE_TAB_SUBGROUPS_AND_PROJECTS, - service: new GroupsService(this.endpoints[ACTIVE_TAB_SUBGROUPS_AND_PROJECTS]), + service: new GroupsService( + this.endpoints[ACTIVE_TAB_SUBGROUPS_AND_PROJECTS], + this.initialSort, + ), store: new GroupsStore({ showSchemaMarkup: true }), + sortingItems: OVERVIEW_TABS_SORTING_ITEMS, }, { title: this.$options.i18n[ACTIVE_TAB_SHARED], key: ACTIVE_TAB_SHARED, - emptyStateComponent: SharedProjectsEmptyState, + emptyStateComponent: markRaw(SharedProjectsEmptyState), lazy: this.$route.name !== ACTIVE_TAB_SHARED, - service: new GroupsService(this.endpoints[ACTIVE_TAB_SHARED]), + service: new GroupsService(this.endpoints[ACTIVE_TAB_SHARED], this.initialSort), store: new GroupsStore(), + sortingItems: OVERVIEW_TABS_SORTING_ITEMS, }, { title: this.$options.i18n[ACTIVE_TAB_ARCHIVED], key: ACTIVE_TAB_ARCHIVED, - emptyStateComponent: ArchivedProjectsEmptyState, + emptyStateComponent: markRaw(ArchivedProjectsEmptyState), lazy: this.$route.name !== ACTIVE_TAB_ARCHIVED, - service: new GroupsService(this.endpoints[ACTIVE_TAB_ARCHIVED]), + service: new ArchivedProjectsService(this.groupId, this.initialSort), store: new GroupsStore(), + sortingItems: OVERVIEW_TABS_ARCHIVED_PROJECTS_SORTING_ITEMS, }, ]; return { @@ -79,15 +88,30 @@ export default { mounted() { this.search = this.$route.query?.filter || ''; - const sortQueryStringValue = this.$route.query?.sort || this.initialSort; - const sort = - OVERVIEW_TABS_SORTING_ITEMS.find((sortOption) => - [sortOption.asc, sortOption.desc].includes(sortQueryStringValue), - ) || SORTING_ITEM_NAME; + const { sort, isAscending } = this.getActiveSort(); + this.sort = sort; - this.isAscending = sort.asc === sortQueryStringValue; + this.isAscending = isAscending; }, methods: { + getActiveSort() { + const sortQueryStringValue = this.$route.query?.sort || this.initialSort; + const sort = this.activeTab.sortingItems.find((sortOption) => + [sortOption.asc, sortOption.desc].includes(sortQueryStringValue), + ); + + if (!sort) { + return { + sort: SORTING_ITEM_NAME, + isAscending: true, + }; + } + + return { + sort, + isAscending: sort.asc === sortQueryStringValue, + }; + }, handleTabInput(tabIndex) { if (tabIndex === this.activeTabIndex) { return; @@ -105,7 +129,23 @@ export default { ? this.$route.params.group.split('/') : this.$route.params.group; - this.$router.push({ name: tab.key, params: { group: groupParam }, query: this.$route.query }); + const { sort, isAscending } = this.getActiveSort(); + + this.sort = sort; + this.isAscending = isAscending; + + const sortQuery = isAscending ? sort.asc : sort.desc; + + const query = { + ...this.$route.query, + ...(this.$route.query?.sort && { sort: sortQuery }), + }; + + this.$router.push({ + name: tab.key, + params: { group: groupParam }, + query, + }); }, handleSearchOrSortChange() { // Update query string @@ -164,7 +204,6 @@ export default { [ACTIVE_TAB_ARCHIVED]: __('Archived projects'), searchPlaceholder: __('Search'), }, - OVERVIEW_TABS_SORTING_ITEMS, }; </script> @@ -203,7 +242,7 @@ export default { @sortDirectionChange="handleSortDirectionChange" > <gl-sorting-item - v-for="sortingItem in $options.OVERVIEW_TABS_SORTING_ITEMS" + v-for="sortingItem in activeTab.sortingItems" :key="sortingItem.label" :active="sortingItem === sort" @click="handleSortingItemClick(sortingItem)" diff --git a/app/assets/javascripts/groups/constants.js b/app/assets/javascripts/groups/constants.js index a5854632040..574ec8e4e49 100644 --- a/app/assets/javascripts/groups/constants.js +++ b/app/assets/javascripts/groups/constants.js @@ -25,25 +25,39 @@ export const ITEM_TYPE = { GROUP: 'group', }; +export const SORTING_ITEM_NAME = { + label: __('Name'), + asc: 'name_asc', + desc: 'name_desc', +}; + +export const SORTING_ITEM_CREATED = { + label: __('Created'), + asc: 'created_asc', + desc: 'created_desc', +}; + +export const SORTING_ITEM_UPDATED = { + label: __('Updated'), + asc: 'latest_activity_asc', + desc: 'latest_activity_desc', +}; + +export const SORTING_ITEM_STARS = { + label: __('Stars'), + asc: 'stars_asc', + desc: 'stars_desc', +}; + export const OVERVIEW_TABS_SORTING_ITEMS = [ - { - label: __('Name'), - asc: 'name_asc', - desc: 'name_desc', - }, - { - label: __('Created'), - asc: 'created_asc', - desc: 'created_desc', - }, - { - label: __('Updated'), - asc: 'latest_activity_asc', - desc: 'latest_activity_desc', - }, - { - label: __('Stars'), - asc: 'stars_asc', - desc: 'stars_desc', - }, + SORTING_ITEM_NAME, + SORTING_ITEM_CREATED, + SORTING_ITEM_UPDATED, + SORTING_ITEM_STARS, +]; + +export const OVERVIEW_TABS_ARCHIVED_PROJECTS_SORTING_ITEMS = [ + SORTING_ITEM_NAME, + SORTING_ITEM_CREATED, + SORTING_ITEM_UPDATED, ]; diff --git a/app/assets/javascripts/groups/init_overview_tabs.js b/app/assets/javascripts/groups/init_overview_tabs.js index ced5d76d8b9..b831ae7b9d6 100644 --- a/app/assets/javascripts/groups/init_overview_tabs.js +++ b/app/assets/javascripts/groups/init_overview_tabs.js @@ -40,6 +40,7 @@ export const initGroupOverviewTabs = () => { const router = createRouter(); const { + groupId, newSubgroupPath, newProjectPath, newSubgroupIllustration, @@ -59,6 +60,7 @@ export const initGroupOverviewTabs = () => { el, router, provide: { + groupId, newSubgroupPath, newProjectPath, newSubgroupIllustration, diff --git a/app/assets/javascripts/groups/service/archived_projects_service.js b/app/assets/javascripts/groups/service/archived_projects_service.js new file mode 100644 index 00000000000..5ffa3f91b06 --- /dev/null +++ b/app/assets/javascripts/groups/service/archived_projects_service.js @@ -0,0 +1,56 @@ +import Api from '~/api'; + +export default class ArchivedProjectsService { + constructor(groupId, initialSort) { + this.groupId = groupId; + this.initialSort = initialSort; + } + + async getGroups(parentId, page, query, sortParam) { + const supportedOrderBy = { + name: 'name', + created: 'created_at', + latest_activity: 'last_activity_at', + }; + + const [, orderBy, sort] = (sortParam || this.initialSort)?.match(/(\w+)_(asc|desc)/) || []; + + const { data: projects, headers } = await Api.groupProjects(this.groupId, query, { + archived: true, + page, + order_by: supportedOrderBy[orderBy], + sort, + }); + + return { + data: projects.map((project) => { + return { + id: project.id, + name: project.name, + full_name: project.name_with_namespace, + markdown_description: project.description_html, + visibility: project.visibility, + avatar_url: project.avatar_url, + relative_path: `/${project.path_with_namespace}`, + edit_path: null, + leave_path: null, + can_edit: false, + can_leave: false, + can_remove: false, + type: 'project', + permission: null, + children: [], + parent_id: project.namespace.id, + project_count: 0, + subgroup_count: 0, + number_users_with_delimiter: 0, + star_count: project.star_count, + updated_at: project.updated_at, + marked_for_deletion: project.marked_for_deletion_at !== null, + last_activity_at: project.last_activity_at, + }; + }), + headers, + }; + } +} diff --git a/app/assets/javascripts/groups/service/groups_service.js b/app/assets/javascripts/groups/service/groups_service.js index 790b581a7c0..28d203bc9c6 100644 --- a/app/assets/javascripts/groups/service/groups_service.js +++ b/app/assets/javascripts/groups/service/groups_service.js @@ -1,11 +1,12 @@ import axios from '~/lib/utils/axios_utils'; export default class GroupsService { - constructor(endpoint) { + constructor(endpoint, initialSort) { this.endpoint = endpoint; + this.initialSort = initialSort; } - getGroups(parentId, page, filterGroups, sort, archived) { + getGroups(parentId, page, filterGroups, sort) { const params = {}; if (parentId) { @@ -20,12 +21,8 @@ export default class GroupsService { params.filter = filterGroups; } - if (sort) { - params.sort = sort; - } - - if (archived) { - params.archived = archived; + if (sort || this.initialSort) { + params.sort = sort || this.initialSort; } } diff --git a/app/assets/javascripts/groups/settings/init_access_dropdown.js b/app/assets/javascripts/groups/settings/init_access_dropdown.js index 24419280fc0..4da38e0e641 100644 --- a/app/assets/javascripts/groups/settings/init_access_dropdown.js +++ b/app/assets/javascripts/groups/settings/init_access_dropdown.js @@ -4,7 +4,7 @@ import AccessDropdown from './components/access_dropdown.vue'; export const initAccessDropdown = (el) => { if (!el) { - return false; + return null; } const { label, disabled, preselectedItems } = el.dataset; |