refactor: identical VDataTable for members on both organisation pages
Both org pages now use the same VDataTable with: - Search field (name/email filter) - Sortable columns (Naam, E-mail, Rol) with default sort on name - Pagination (10 per page) - Avatar with initials, role chips with color mapping - Consistent empty state with icon Platform page: replaced VTable with VDataTable, added role chips (replacing inline AppSelect), role editing via menu on edit button. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -95,18 +95,22 @@ const roleLabelMap: Record<string, string> = {
|
||||
volunteer_coordinator: 'Vrijwilliger Coördinator',
|
||||
}
|
||||
|
||||
const memberSearch = ref('')
|
||||
|
||||
const memberHeaders = computed(() => {
|
||||
const headers: Array<{ title: string; key: string; sortable?: boolean }> = [
|
||||
{ title: 'Naam', key: 'full_name' },
|
||||
{ title: 'E-mailadres', key: 'email' },
|
||||
{ title: 'Rol', key: 'role' },
|
||||
const headers: Array<{ title: string; key: string; sortable?: boolean; align?: 'start' | 'end' }> = [
|
||||
{ title: 'Naam', key: 'full_name', sortable: true },
|
||||
{ title: 'E-mailadres', key: 'email', sortable: true },
|
||||
{ title: 'Rol', key: 'role', sortable: true },
|
||||
]
|
||||
if (isOrgAdmin.value) {
|
||||
headers.push({ title: 'Acties', key: 'actions', sortable: false })
|
||||
headers.push({ title: 'Acties', key: 'actions', sortable: false, align: 'end' })
|
||||
}
|
||||
return headers
|
||||
})
|
||||
|
||||
const memberSortBy = [{ key: 'full_name', order: 'asc' as const }]
|
||||
|
||||
function getInitials(name: string): string {
|
||||
return name
|
||||
.split(' ')
|
||||
@@ -328,32 +332,43 @@ function confirmRevokeInvitation() {
|
||||
</VAlert>
|
||||
|
||||
<template v-else>
|
||||
<!-- Invite button -->
|
||||
<div
|
||||
v-if="isOrgAdmin"
|
||||
class="d-flex justify-end mb-4"
|
||||
>
|
||||
<VBtn
|
||||
prepend-icon="tabler-user-plus"
|
||||
@click="isInviteDialogOpen = true"
|
||||
>
|
||||
Lid uitnodigen
|
||||
</VBtn>
|
||||
</div>
|
||||
|
||||
<!-- Members table -->
|
||||
<VCard class="mb-6">
|
||||
<VCardText v-if="members.length === 0">
|
||||
<p class="text-body-1 text-disabled mb-0">
|
||||
Nog geen leden
|
||||
</p>
|
||||
<!-- Search + Invite -->
|
||||
<VCardText>
|
||||
<VRow>
|
||||
<VCol
|
||||
cols="12"
|
||||
md="6"
|
||||
>
|
||||
<AppTextField
|
||||
v-model="memberSearch"
|
||||
placeholder="Zoek op naam of e-mail..."
|
||||
prepend-inner-icon="tabler-search"
|
||||
clearable
|
||||
/>
|
||||
</VCol>
|
||||
<VCol
|
||||
v-if="isOrgAdmin"
|
||||
cols="12"
|
||||
md="6"
|
||||
class="d-flex justify-end"
|
||||
>
|
||||
<VBtn
|
||||
prepend-icon="tabler-user-plus"
|
||||
@click="isInviteDialogOpen = true"
|
||||
>
|
||||
Lid uitnodigen
|
||||
</VBtn>
|
||||
</VCol>
|
||||
</VRow>
|
||||
</VCardText>
|
||||
|
||||
<VDataTable
|
||||
v-else
|
||||
:headers="memberHeaders"
|
||||
:items="members"
|
||||
:items-per-page="-1"
|
||||
hide-default-footer
|
||||
:search="memberSearch"
|
||||
:sort-by="memberSortBy"
|
||||
:items-per-page="10"
|
||||
>
|
||||
<template #item.full_name="{ item }">
|
||||
<div class="d-flex align-center gap-x-3">
|
||||
@@ -384,7 +399,7 @@ function confirmRevokeInvitation() {
|
||||
</template>
|
||||
|
||||
<template #item.actions="{ item }">
|
||||
<div class="d-flex gap-x-1">
|
||||
<div class="d-flex gap-x-1 justify-end">
|
||||
<VBtn
|
||||
icon="tabler-edit"
|
||||
variant="text"
|
||||
@@ -406,6 +421,17 @@ function confirmRevokeInvitation() {
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<template #no-data>
|
||||
<div class="text-center pa-4 text-disabled">
|
||||
<VIcon
|
||||
icon="tabler-users-minus"
|
||||
size="48"
|
||||
class="mb-2"
|
||||
/>
|
||||
<p>Geen leden gevonden</p>
|
||||
</div>
|
||||
</template>
|
||||
</VDataTable>
|
||||
</VCard>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user