Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

admin settings pagination #9119

Merged
merged 10 commits into from
May 30, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
Enhancement: Support pagination in admin settings app

We've added pagination to various lists in the admin settings app.
So there will be a page selection at the end of the list if more than 50 items are present.

https://github.com/owncloud/web/issues/9048
https://github.com/owncloud/web/pull/9119
101 changes: 66 additions & 35 deletions packages/web-app-admin-settings/src/components/Groups/GroupsList.vue
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
:sort-by="sortBy"
:sort-dir="sortDir"
:fields="fields"
:data="data"
:data="paginatedItems"
:highlighted="highlighted"
:sticky="true"
:header-position="fileListHeaderY"
Expand All @@ -28,7 +28,9 @@
:label="$gettext('Select all groups')"
:model-value="allGroupsSelected"
hide-label
@update:model-value="$emit('toggleSelectAllGroups')"
@update:model-value="
allGroupsSelected ? $emit('unSelectAllGroups') : $emit('selectGroups', paginatedItems)
"
/>
</template>
<template #select="rowData">
Expand Down Expand Up @@ -93,6 +95,7 @@
</context-menu-quick-action>
</template>
<template #footer>
<pagination :pages="paginationPages" :current-page="currentPage" />
<div class="oc-text-nowrap oc-text-center oc-width-1-1 oc-my-s">
<p class="oc-text-muted">{{ footerTextTotal }}</p>
<p v-if="filterTerm" class="oc-text-muted">{{ footerTextFilter }}</p>
Expand All @@ -103,20 +106,30 @@
</template>

<script lang="ts">
import { defineComponent, PropType, ref, unref, ComponentPublicInstance, computed } from 'vue'
import {
defineComponent,
PropType,
ref,
unref,
ComponentPublicInstance,
computed,
watch
} from 'vue'
import Fuse from 'fuse.js'
import Mark from 'mark.js'
import { displayPositionedDropdown, eventBus } from 'web-pkg'
import { displayPositionedDropdown, eventBus, SortDir } from 'web-pkg'
import { SideBarEventTopics } from 'web-pkg/src/composables/sideBar'
import { Group } from 'web-client/src/generated'
import ContextMenuQuickAction from 'web-pkg/src/components/ContextActions/ContextMenuQuickAction.vue'
import { useGettext } from 'vue3-gettext'
import { defaultFuseOptions } from 'web-pkg/src/helpers'
import { useFileListHeaderPosition } from 'web-pkg/src/composables'
import Pagination from 'web-pkg/src/components/Pagination.vue'
import { usePagination } from 'web-app-admin-settings/src/composables'

export default defineComponent({
name: 'GroupsList',
components: { ContextMenuQuickAction },
components: { ContextMenuQuickAction, Pagination },
props: {
groups: {
type: Array as PropType<Group[]>,
Expand All @@ -127,11 +140,14 @@ export default defineComponent({
required: true
}
},
emits: ['toggleSelectAllGroups', 'unSelectAllGroups', 'toggleSelectGroup'],
emits: ['selectGroups', 'unSelectAllGroups', 'toggleSelectGroup'],
setup(props, { emit }) {
const { $gettext } = useGettext()
const { y: fileListHeaderY } = useFileListHeaderPosition('#admin-settings-app-bar')
const contextMenuButtonRef = ref(undefined)
const sortBy = ref<string>('displayName')
const sortDir = ref<string>(SortDir.Asc)
const filterTerm = ref<string>('')

const isGroupSelected = (group) => {
return props.selectedGroups.some((s) => s.id === group.id)
Expand All @@ -140,6 +156,7 @@ export default defineComponent({
emit('unSelectAllGroups')
emit('toggleSelectGroup', group)
}

const showDetails = (group) => {
if (!isGroupSelected(group)) {
selectGroup(group)
Expand Down Expand Up @@ -183,6 +200,36 @@ export default defineComponent({

const readOnlyLabel = computed(() => $gettext("This group is read-only and can't be edited"))

const filter = (groups: Group[], filterTerm: string) => {
if (!(filterTerm || '').trim()) {
return groups
}
const groupsSearchEngine = new Fuse(groups, { ...defaultFuseOptions, keys: ['displayName'] })
return groupsSearchEngine.search(filterTerm).map((r) => r.item)
}

const orderBy = (list, prop, desc) => {
return [...list].sort((a, b) => {
a = a[prop]?.toString() || ''
b = b[prop]?.toString() || ''
return desc ? b.localeCompare(a) : a.localeCompare(b)
})
}

const items = computed(() => {
return orderBy(
filter(props.groups, unref(filterTerm)),
unref(sortBy),
unref(sortDir) === SortDir.Desc
)
})

const pagination = usePagination({ items })

watch(pagination.currentPage, () => {
emit('unSelectAllGroups')
})

return {
showDetails,
rowClicked,
Expand All @@ -192,15 +239,19 @@ export default defineComponent({
fileListHeaderY,
contextMenuButtonRef,
showEditPanel,
readOnlyLabel
readOnlyLabel,
filterTerm,
sortBy,
sortDir,
items,
...pagination,
filter,
orderBy
}
},
data() {
return {
sortBy: 'displayName',
sortDir: 'asc',
markInstance: null,
filterTerm: ''
markInstance: null
}
},
computed: {
Expand Down Expand Up @@ -241,7 +292,7 @@ export default defineComponent({
]
},
allGroupsSelected() {
return this.groups.length === this.selectedGroups.length
return this.paginatedItems.length === this.selectedGroups.length
},
footerTextTotal() {
return this.$gettext('%{groupCount} groups in total', {
Expand All @@ -250,25 +301,19 @@ export default defineComponent({
},
footerTextFilter() {
return this.$gettext('%{groupCount} matching groups', {
groupCount: this.data.length.toString()
groupCount: this.items.length.toString()
})
},
data() {
return this.orderBy(
this.filter(this.groups, this.filterTerm),
this.sortBy,
this.sortDir === 'desc'
)
},
highlighted() {
return this.selectedGroups.map((group) => group.id)
}
},
watch: {
filterTerm() {
async filterTerm() {
if (!this.markInstance) {
return
}
await this.$router.push({ ...this.$route, query: { ...this.$route.query, page: '1' } })
this.markInstance.unmark()
this.markInstance.mark(this.filterTerm, {
element: 'span',
Expand All @@ -283,20 +328,6 @@ export default defineComponent({
})
},
methods: {
filter(groups, filterTerm) {
if (!(filterTerm || '').trim()) {
return groups
}
const groupsSearchEngine = new Fuse(groups, { ...defaultFuseOptions, keys: ['displayName'] })
return groupsSearchEngine.search(filterTerm).map((r) => r.item)
},
orderBy(list, prop, desc) {
return [...list].sort((a, b) => {
a = a[prop]?.toString() || ''
b = b[prop]?.toString() || ''
return desc ? b.localeCompare(a) : a.localeCompare(b)
})
},
handleSort(event) {
this.sortBy = event.sortBy
this.sortDir = event.sortDir
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
:sort-by="sortBy"
:sort-dir="sortDir"
:fields="fields"
:data="orderedSpaces"
:data="paginatedItems"
:highlighted="highlighted"
:sticky="true"
:header-position="fileListHeaderY"
Expand All @@ -29,7 +29,9 @@
:label="$gettext('Select all spaces')"
:model-value="allSpacesSelected"
hide-label
@update:model-value="$emit('toggleSelectAllSpaces')"
@update:model-value="
allSpacesSelected ? $emit('unSelectAllSpaces') : $emit('selectSpaces', paginatedItems)
"
/>
</template>
<template #select="{ item }">
Expand Down Expand Up @@ -105,6 +107,7 @@
</div>
</template>
<template #footer>
<pagination :pages="paginationPages" :current-page="currentPage" />
<div class="oc-text-nowrap oc-text-center oc-width-1-1 oc-my-s">
<p class="oc-text-muted">{{ footerTextTotal }}</p>
<p v-if="filterTerm" class="oc-text-muted">{{ footerTextFilter }}</p>
Expand All @@ -128,14 +131,16 @@ import { spaceRoleEditor, spaceRoleManager, spaceRoleViewer } from 'web-client/s
import Mark from 'mark.js'
import Fuse from 'fuse.js'
import { useGettext } from 'vue3-gettext'
import { eventBus } from 'web-pkg'
import { eventBus, SortDir } from 'web-pkg'
import { SideBarEventTopics } from 'web-pkg/src/composables/sideBar'
import ContextMenuQuickAction from 'web-pkg/src/components/ContextActions/ContextMenuQuickAction.vue'
import { useFileListHeaderPosition } from 'web-pkg/src/composables'
import { useFileListHeaderPosition, useRoute, useRouter } from 'web-pkg/src/composables'
import { usePagination } from 'web-app-admin-settings/src/composables'
import Pagination from 'web-pkg/src/components/Pagination.vue'

export default defineComponent({
name: 'SpacesList',
components: { ContextMenuQuickAction },
components: { ContextMenuQuickAction, Pagination },
props: {
spaces: {
type: Array as PropType<SpaceResource[]>,
Expand All @@ -146,18 +151,20 @@ export default defineComponent({
required: true
}
},
emits: ['toggleSelectSpace', 'toggleSelectAllSpaces', 'unSelectAllSpaces'],
emits: ['toggleSelectSpace', 'selectSpaces', 'unSelectAllSpaces'],
setup: function (props, { emit }) {
const router = useRouter()
const route = useRoute()
const { $gettext, current: currentLanguage } = useGettext()

const { y: fileListHeaderY } = useFileListHeaderPosition('#admin-settings-app-bar')
const contextMenuButtonRef = ref(undefined)
const sortBy = ref('name')
const sortDir = ref('asc')
const sortDir = ref(SortDir.Asc)
const filterTerm = ref('')
const markInstance = ref(undefined)
const tableRef = ref(undefined)

const allSpacesSelected = computed(() => props.spaces.length === props.selectedSpaces.length)
const highlighted = computed(() => props.selectedSpaces.map((s) => s.id))
const footerTextTotal = computed(() => {
return $gettext('%{spaceCount} spaces in total', {
Expand All @@ -166,7 +173,7 @@ export default defineComponent({
})
const footerTextFilter = computed(() => {
return $gettext('%{spaceCount} matching spaces', {
spaceCount: unref(orderedSpaces).length.toString()
spaceCount: unref(items).length.toString()
})
})

Expand Down Expand Up @@ -206,9 +213,24 @@ export default defineComponent({
: a.localeCompare(b, undefined, { numeric })
})
}
const orderedSpaces = computed(() =>
orderBy(filter(props.spaces, unref(filterTerm)), unref(sortBy), unref(sortDir) === 'desc')
const items = computed(() =>
orderBy(
filter(props.spaces, unref(filterTerm)),
unref(sortBy),
unref(sortDir) === SortDir.Desc
)
)

const pagination = usePagination({ items })

watch(pagination.currentPage, () => {
emit('unSelectAllSpaces')
})

const allSpacesSelected = computed(() => {
return unref(pagination.paginatedItems).length === props.selectedSpaces.length
})

const handleSort = (event) => {
sortBy.value = event.sortBy
sortDir.value = event.sortDir
Expand Down Expand Up @@ -347,11 +369,12 @@ export default defineComponent({
})
})

watch(filterTerm, () => {
watch(filterTerm, async () => {
const instance = unref(markInstance)
if (!instance) {
return
}
await router.push({ ...unref(route), query: { ...unref(route).query, page: '1' } })
instance.unmark()
instance.mark(unref(filterTerm), {
element: 'span',
Expand Down Expand Up @@ -417,15 +440,16 @@ export default defineComponent({
getMemberCount,
getSelectSpaceLabel,
handleSort,
orderedSpaces,
fileClicked,
isSpaceSelected,
contextMenuButtonRef,
showContextMenuOnBtnClick,
showContextMenuOnRightClick,
spaceDetailsLabel,
showDetailsForSpace,
fileListHeaderY
fileListHeaderY,
items,
...pagination
}
}
})
Expand Down
Loading