Skip to content
Open
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
10 changes: 6 additions & 4 deletions web-app/packages/admin-lib/src/modules/admin/adminApi.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,10 @@ export const AdminApi = {
},

async fetchUsers(
params: PaginatedUsersParams
params: PaginatedUsersParams,
signal?: AbortSignal
): Promise<AxiosResponse<UsersResponse>> {
return AdminModule.httpService.get(`/app/admin/users`, { params })
return AdminModule.httpService.get(`/app/admin/users`, { params, signal })
},

async fetchUserByName(
Expand Down Expand Up @@ -73,9 +74,10 @@ export const AdminApi = {
},

async getProjects(
params: PaginatedAdminProjectsParams
params: PaginatedAdminProjectsParams,
signal?: AbortSignal
): Promise<AxiosResponse<PaginatedAdminProjectsResponse>> {
return AdminModule.httpService.get('/app/admin/projects', { params })
return AdminModule.httpService.get('/app/admin/projects', { params, signal })
},

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
<PInputText
placeholder="Search accounts"
data-cy="search-members-field"
v-model="searchByName"
v-model="search"
class="w-full"
@input="onSearch"
/>
Expand All @@ -44,49 +44,45 @@
:first="(options.page - 1) * options.itemsPerPage"
:sort-field="options.sortBy[0]"
:sort-order="options.sortDesc[0] ? -1 : 1"
:rowHover="true"
removableSort
reorderable-columns
@page="onPage"
@row-click="rowClick"
@sort="onSort"
data-cy="accounts-table"
>
<template v-for="header in headers" :key="header.field">
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This means also for other tables. We could probably live with this config json as we can then extend it and have one default column when slot is not used.

<PColumn
v-if="header.field === 'username'"
:field="header.field"
:header="header.header"
:sortable="header.sortable"
>
<template #body="slotProps">
<router-link
class="title-t4"
:to="{
name: 'account',
params: { username: slotProps.data.username }
}"
>
{{ slotProps.data.username }}
</router-link>
</template>
</PColumn>
<PColumn
v-else-if="header.field === 'active'"
:header="header.header"
:field="header.field"
>
<template #body="slotProps">
<i v-if="slotProps.data.active" class="ti ti-check" />
<PColumn field="username" header="Username" :sortable="true">
<template #body="{ data }">
<router-link
:to="accountRoute(data)"
class="dt-row-link title-t4"
>
{{ data.username }}
</router-link>
</template>
</PColumn>
<PColumn field="email" header="Email" :sortable="true">
<template #body="{ data }">
<router-link :to="accountRoute(data)" class="dt-row-link">
{{ data.email }}
</router-link>
</template>
</PColumn>
<PColumn field="profile.name" header="Full name">
<template #body="{ data }">
<router-link :to="accountRoute(data)" class="dt-row-link">
{{ data.profile?.name }}
</router-link>
</template>
</PColumn>
<PColumn field="active" header="Active">
<template #body="{ data }">
<router-link :to="accountRoute(data)" class="dt-row-link">
<i v-if="data.active" class="ti ti-check" />
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just note from some users. We could add here just text Active / Inactive as icon is not copy able.

<i v-else class="ti ti-x" />
</template>
</PColumn>
<PColumn
v-else
:field="header.field"
:header="header.header"
:sortable="header.sortable"
></PColumn>
</template>
</router-link>
</template>
</PColumn>
<template #paginatorstart>
<PButton
icon="ti ti-refresh"
Expand All @@ -106,18 +102,12 @@
<script lang="ts">
import {
PaginatedUsersParams,
useDataTableSearch,
useDialogStore,
TableDataHeader,
AppContainer,
AppSection
} from '@mergin/lib'
import debounce from 'lodash/debounce'
import { mapActions, mapState } from 'pinia'
import {
DataTablePageEvent,
DataTableRowClickEvent,
DataTableSortEvent
} from 'primevue/datatable'
import { mapState } from 'pinia'
import { defineComponent } from 'vue'

import { AdminRoutes } from '@/modules'
Expand All @@ -130,88 +120,52 @@ export default defineComponent({
AppContainer,
AppSection
},
data() {
setup() {
const adminStore = useAdminStore()
const dialogStore = useDialogStore()

const tableSearch = useDataTableSearch({
defaultSortBy: 'username',
defaultSortDesc: false
})

tableSearch.setFetchFn((signal) => {
const { options, search } = tableSearch
const params: PaginatedUsersParams = {
page: options.page,
per_page: options.itemsPerPage
}
if (options.sortBy[0]) {
params.descending = options.sortDesc[0]
params.order_by = options.sortBy[0]
}
if (search.value) params.like = search.value.trim()
adminStore.fetchUsers({ params, signal })
})

return {
options: {
sortBy: ['username'],
sortDesc: [false],
itemsPerPage: 20,
page: 1,
perPageOptions: [20, 50, 100]
},
searchByName: '',
headers: [
{ field: 'username', header: 'Username', sortable: true },
{ field: 'email', header: 'Email', sortable: true },
{ field: 'profile.name', header: 'Full name' },
{ field: 'active', header: 'Active' }
] as TableDataHeader[]
...tableSearch,
show: dialogStore.show.bind(dialogStore)
}
},
computed: {
...mapState(useAdminStore, ['users', 'loading'])
},
created() {
this.resetPaging = debounce(this.resetPaging, 1000)
this.fetchUsers({ params: this.getParams() })
this.initFromQuery()
this.doFetch()
},
methods: {
...mapActions(useAdminStore, ['fetchUsers']),
...mapActions(useDialogStore, ['show']),

onSearch() {
this.resetPaging()
this.fetchUsers({ params: this.getParams() })
},

async resetPaging() {
this.options.page = 1
},

getParams(): PaginatedUsersParams {
const params = {
page: this.options.page,
per_page: this.options.itemsPerPage
} as PaginatedUsersParams
if (this.options.sortBy[0]) {
params.descending = this.options.sortDesc[0]
params.order_by = this.options.sortBy[0]
}
if (this.searchByName) {
params.like = this.searchByName.trim()
}
return params
},

onRefresh() {
this.fetchUsers({ params: this.getParams() })
},

onPage(event: DataTablePageEvent) {
this.options.page = event.page + 1
this.options.itemsPerPage = event.rows
this.fetchUsers({ params: this.getParams() })
},

onSort(event: DataTableSortEvent) {
this.options.sortBy[0] = event.sortField?.toString()
this.options.sortDesc[0] = event.sortOrder < 1
this.fetchUsers({ params: this.getParams() })
},

rowClick(event: DataTableRowClickEvent) {
this.$router.push({
name: AdminRoutes.ACCOUNT,
params: { username: event.data.username }
})
accountRoute(data) {
return { name: AdminRoutes.ACCOUNT, params: { username: data.username } }
},

createUserDialog() {
const dialog = { maxWidth: 500, header: 'Create user' }
const listeners = {
success: () => {
this.resetPaging()
this.fetchUsers({ params: this.getParams() })
this.options.page = 1
this.doFetch()
}
}
this.show({
Expand Down
Loading
Loading