add new group table and filter for member table
This commit is contained in:
@@ -5,10 +5,19 @@ import { useNotify } from 'src/vueLib/general/useNotify';
|
||||
import { i18n } from 'boot/lang';
|
||||
import { useResponsibleTable } from '../responsible/ResponsibleTable';
|
||||
import { appName } from 'src/vueLib/models/settings';
|
||||
import { useGroupTable } from '../group/GroupTable';
|
||||
|
||||
export function useMemberTable() {
|
||||
const members = ref<Members>([]);
|
||||
const allMembers = ref<Members>([]);
|
||||
const filteredMembers = ref<Members>([]);
|
||||
const filterList = ref<
|
||||
{
|
||||
field: keyof Member;
|
||||
keys: string[];
|
||||
}[]
|
||||
>();
|
||||
const { responsibles, updateResponsibles } = useResponsibleTable();
|
||||
const { groups, updateGroups } = useGroupTable();
|
||||
|
||||
const pagination = ref({
|
||||
sortBy: 'firstName',
|
||||
@@ -202,20 +211,20 @@ export function useMemberTable() {
|
||||
loading.value = true;
|
||||
|
||||
await updateResponsibles().catch((err) => NotifyResponse(err, 'error'));
|
||||
|
||||
appApi
|
||||
await updateGroups().catch((err) => NotifyResponse(err, 'error'));
|
||||
await appApi
|
||||
.get('members')
|
||||
.then((resp) => {
|
||||
if (resp.data === null) {
|
||||
members.value = [];
|
||||
allMembers.value = [];
|
||||
return;
|
||||
}
|
||||
members.value = resp.data as Members;
|
||||
if (members.value === null) {
|
||||
members.value = [];
|
||||
allMembers.value = resp.data as Members;
|
||||
if (allMembers.value === null) {
|
||||
allMembers.value = [];
|
||||
return;
|
||||
}
|
||||
members.value.forEach((member) => {
|
||||
allMembers.value.forEach((member) => {
|
||||
if (!responsibles.value.some((r) => r.id === member.responsiblePerson?.id)) {
|
||||
delete member.responsiblePerson;
|
||||
}
|
||||
@@ -233,7 +242,7 @@ export function useMemberTable() {
|
||||
loading.value = false;
|
||||
//filter same members out so list is shorter
|
||||
if (filter) {
|
||||
members.value = members.value.filter(
|
||||
filteredMembers.value = allMembers.value.filter(
|
||||
(m1) =>
|
||||
!filter.some((m2) => {
|
||||
if (filterbyName) {
|
||||
@@ -243,9 +252,45 @@ export function useMemberTable() {
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
//second filter
|
||||
const list = filterList.value ?? [];
|
||||
if (filterList.value && filterList.value.length > 0) {
|
||||
filteredMembers.value = allMembers.value.filter((member) =>
|
||||
list.every((filterItem) => {
|
||||
const keys = filterItem.keys ?? [];
|
||||
if (keys.includes('null')) return true;
|
||||
if (keys.length === 0) return true;
|
||||
|
||||
const value = member[filterItem.field];
|
||||
|
||||
if (value === undefined || value === null) {
|
||||
return keys.includes('None');
|
||||
}
|
||||
if (typeof value === 'number') {
|
||||
return keys.includes(value.toString());
|
||||
}
|
||||
if (typeof value === 'string') {
|
||||
return keys.includes(value);
|
||||
}
|
||||
return false;
|
||||
}),
|
||||
);
|
||||
return;
|
||||
}
|
||||
filteredMembers.value = allMembers.value;
|
||||
});
|
||||
}
|
||||
|
||||
function setNewFilter(field: string, ...keys: string[]) {
|
||||
filterList.value = [
|
||||
{
|
||||
field: field as keyof Member,
|
||||
keys: keys.flat().map((k) => String(k)),
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
function disableColumns(...columns: string[]) {
|
||||
columns.forEach((col) => {
|
||||
if (col in enabledColumns.value) {
|
||||
@@ -265,7 +310,7 @@ export function useMemberTable() {
|
||||
const header = exportableColumns.map((col) => col.field).join(comma);
|
||||
|
||||
// Build CSV rows
|
||||
const data = members.value.map((member) =>
|
||||
const data = allMembers.value.map((member) =>
|
||||
exportableColumns
|
||||
.map((col) => {
|
||||
const value = member[col.field];
|
||||
@@ -296,13 +341,16 @@ export function useMemberTable() {
|
||||
}
|
||||
|
||||
return {
|
||||
members,
|
||||
allMembers,
|
||||
filteredMembers,
|
||||
responsibles,
|
||||
groups,
|
||||
pagination,
|
||||
columns,
|
||||
loading,
|
||||
getRowClass,
|
||||
updateMembers,
|
||||
setNewFilter,
|
||||
isXDaysBeforeAnnualDate,
|
||||
disableColumns,
|
||||
exportCsv,
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
:loading-label="$t('loading')"
|
||||
:rows-per-page-label="$t('recordsPerPage')"
|
||||
:selected-rows-label="(val) => val + ' ' + $t('recordSelected')"
|
||||
:rows="members"
|
||||
:rows="filteredMembers"
|
||||
:columns="columns"
|
||||
row-key="id"
|
||||
v-model:pagination="pagination"
|
||||
@@ -22,71 +22,106 @@
|
||||
class="bigger-table-text"
|
||||
>
|
||||
<template v-slot:top-left>
|
||||
<q-btn-group push flat style="color: grey">
|
||||
<q-btn
|
||||
v-if="user.isPermittedTo('members', 'write')"
|
||||
dense
|
||||
flat
|
||||
icon="add"
|
||||
@click="openAllValueDialog(null)"
|
||||
>
|
||||
<q-tooltip>{{ $t('addNewMember') }}</q-tooltip>
|
||||
</q-btn>
|
||||
<q-btn
|
||||
v-if="user.isPermittedTo('members', 'write') || user.isPermittedTo('members', 'delete')"
|
||||
dense
|
||||
flat
|
||||
style="color: grey"
|
||||
:icon="selectOption ? 'check_box' : 'check_box_outline_blank'"
|
||||
@click="selectOption = !selectOption"
|
||||
>
|
||||
<q-tooltip>{{ $t('selectMemberOptions') }}</q-tooltip>
|
||||
</q-btn>
|
||||
<q-btn
|
||||
v-if="user.isPermittedTo('members', 'import')"
|
||||
dense
|
||||
flat
|
||||
icon="upload"
|
||||
@click="openUploadDialog"
|
||||
>
|
||||
<q-tooltip>{{ $t('importCSV') }}</q-tooltip>
|
||||
</q-btn>
|
||||
<q-btn
|
||||
v-if="user.isPermittedTo('members', 'export')"
|
||||
dense
|
||||
flat
|
||||
icon="download"
|
||||
@click="exportCsv"
|
||||
>
|
||||
<q-tooltip>{{ $t('exportCSV') }}</q-tooltip>
|
||||
</q-btn>
|
||||
</q-btn-group>
|
||||
<div v-if="selectOption && selected.length > 0">
|
||||
<q-btn
|
||||
v-if="inProps.addAttendees || inProps.addResponsible"
|
||||
dense
|
||||
color="grey-7"
|
||||
flat
|
||||
icon="person"
|
||||
@click="addMemberTo"
|
||||
>
|
||||
<q-badge floating transparent color="primary" text-color="primary-text">+</q-badge>
|
||||
</q-btn>
|
||||
<q-btn v-else flat dense icon="more_vert" @click="openSubmenu = true" />
|
||||
|
||||
<q-menu v-if="openSubmenu" anchor="bottom middle" self="top middle">
|
||||
<q-item clickable v-close-popup @click="addToEvent" class="text-primary">{{
|
||||
$t('addToEvent')
|
||||
}}</q-item>
|
||||
<q-item
|
||||
v-if="user.isPermittedTo('members', 'delete')"
|
||||
clickable
|
||||
v-close-popup
|
||||
@click="openRemoveDialog(...selected)"
|
||||
class="text-negative"
|
||||
>{{ $t('delete') }}</q-item
|
||||
<div>
|
||||
<q-btn-group push flat style="color: grey">
|
||||
<q-btn
|
||||
v-if="user.isPermittedTo('members', 'write')"
|
||||
dense
|
||||
flat
|
||||
icon="add"
|
||||
@click="openAllValueDialog(null)"
|
||||
>
|
||||
</q-menu>
|
||||
<q-tooltip>{{ $t('addNewMember') }}</q-tooltip>
|
||||
</q-btn>
|
||||
<q-btn
|
||||
v-if="
|
||||
user.isPermittedTo('members', 'write') || user.isPermittedTo('members', 'delete')
|
||||
"
|
||||
dense
|
||||
flat
|
||||
style="color: grey"
|
||||
:icon="selectOption ? 'check_box' : 'check_box_outline_blank'"
|
||||
@click="selectOption = !selectOption"
|
||||
>
|
||||
<q-tooltip>{{ $t('selectMemberOptions') }}</q-tooltip>
|
||||
</q-btn>
|
||||
<q-btn
|
||||
v-if="user.isPermittedTo('members', 'import')"
|
||||
dense
|
||||
flat
|
||||
icon="upload"
|
||||
@click="openUploadDialog"
|
||||
>
|
||||
<q-tooltip>{{ $t('importCSV') }}</q-tooltip>
|
||||
</q-btn>
|
||||
<q-btn
|
||||
v-if="user.isPermittedTo('members', 'export')"
|
||||
dense
|
||||
flat
|
||||
icon="download"
|
||||
@click="exportCsv"
|
||||
>
|
||||
<q-tooltip>{{ $t('exportCSV') }}</q-tooltip>
|
||||
</q-btn>
|
||||
</q-btn-group>
|
||||
<div v-if="selectOption && selected.length > 0">
|
||||
<q-btn
|
||||
v-if="inProps.addAttendees || inProps.addResponsible"
|
||||
dense
|
||||
color="grey-7"
|
||||
flat
|
||||
icon="person"
|
||||
@click="addMemberTo"
|
||||
>
|
||||
<q-badge floating transparent color="primary" text-color="primary-text">+</q-badge>
|
||||
</q-btn>
|
||||
<q-btn v-else flat dense icon="more_vert" @click="openSubmenu = true" />
|
||||
|
||||
<q-menu v-if="openSubmenu" anchor="bottom middle" self="top middle">
|
||||
<q-item clickable v-close-popup @click="addToEvent" class="text-primary">{{
|
||||
$t('addToEvent')
|
||||
}}</q-item>
|
||||
<q-item
|
||||
v-if="user.isPermittedTo('members', 'delete')"
|
||||
clickable
|
||||
v-close-popup
|
||||
@click="openRemoveDialog(...selected)"
|
||||
class="text-negative"
|
||||
>{{ $t('delete') }}</q-item
|
||||
>
|
||||
</q-menu>
|
||||
</div>
|
||||
<q-card flat class="q-pa-sm">
|
||||
<q-select
|
||||
:label="$t('filterByColumn')"
|
||||
dense
|
||||
v-model="selectedColumnFilter"
|
||||
option-label="label"
|
||||
option-value="name"
|
||||
map-options
|
||||
emit-value
|
||||
clearable
|
||||
:options="columns.filter((col) => col.label !== '')"
|
||||
v-on:clear="selectedColumnOptions = []"
|
||||
@update:model-value="
|
||||
filterMembers(selectedColumnFilter, ...(selectedColumnOptions || []))
|
||||
"
|
||||
class="q-mt-xs"
|
||||
/>
|
||||
<q-select
|
||||
v-if="selectedColumnFilter"
|
||||
:label="$t('filterByColumnValue')"
|
||||
dense
|
||||
v-model="selectedColumnOptions"
|
||||
:options="setColumnOptions(selectedColumnFilter)"
|
||||
class="q-mt-xs"
|
||||
multiple
|
||||
clearable
|
||||
@update:model-value="
|
||||
filterMembers(selectedColumnFilter, ...(selectedColumnOptions || []))
|
||||
"
|
||||
/>
|
||||
</q-card>
|
||||
</div>
|
||||
<div v-if="selectOption && selected.length > 0" class="text-weight-bold">
|
||||
{{ $t('selected') }}: {{ selected.length }}
|
||||
@@ -121,6 +156,21 @@
|
||||
/>
|
||||
</q-td>
|
||||
</template>
|
||||
<template v-slot:body-cell-group="props">
|
||||
<q-td :props="props">
|
||||
<q-select
|
||||
v-if="groups.length > 0"
|
||||
:readonly="!user.isPermittedTo('members', 'write')"
|
||||
:options="groups"
|
||||
emit-value
|
||||
map-options
|
||||
option-value="name"
|
||||
option-label="name"
|
||||
v-model="props.row.group"
|
||||
@update:model-value="updateMember(props.row)"
|
||||
></q-select>
|
||||
</q-td>
|
||||
</template>
|
||||
<template v-slot:body-cell-responsiblePerson="props">
|
||||
<q-td :props="props">
|
||||
<q-select
|
||||
@@ -213,6 +263,7 @@ import AddToEvent from 'src/components/AddToEvent.vue';
|
||||
import { databaseName } from 'src/vueLib/models/settings';
|
||||
import { useUserStore } from 'src/vueLib/login/userStore';
|
||||
import { i18n } from 'src/boot/lang';
|
||||
import { getLocalPageDefaults, setLocalPageDefaults } from 'src/localstorage/localStorage';
|
||||
|
||||
const inProps = defineProps({
|
||||
addAttendees: { type: Boolean },
|
||||
@@ -238,15 +289,22 @@ const selected = ref<Members>([]);
|
||||
const openSubmenu = ref(false);
|
||||
const filter = ref('');
|
||||
const user = useUserStore();
|
||||
const localCompareMembers = ref<Members>();
|
||||
const selectedColumnFilter = ref<string>('');
|
||||
const selectedColumnOptions = ref<string[]>([]);
|
||||
const page = ref<string>('members');
|
||||
|
||||
const {
|
||||
members,
|
||||
allMembers,
|
||||
filteredMembers,
|
||||
responsibles,
|
||||
groups,
|
||||
pagination,
|
||||
loading,
|
||||
columns,
|
||||
getRowClass,
|
||||
updateMembers,
|
||||
setNewFilter,
|
||||
isXDaysBeforeAnnualDate,
|
||||
disableColumns,
|
||||
exportCsv,
|
||||
@@ -254,6 +312,7 @@ const {
|
||||
|
||||
//load on mounting page
|
||||
onMounted(() => {
|
||||
page.value = 'members';
|
||||
if (inProps.addAttendees || inProps.addResponsible) {
|
||||
selectOption.value = true;
|
||||
disableColumns(
|
||||
@@ -265,15 +324,21 @@ onMounted(() => {
|
||||
'email',
|
||||
'address',
|
||||
'phone',
|
||||
'group',
|
||||
'responsiblePerson',
|
||||
'firstVisit',
|
||||
'lastVisit',
|
||||
);
|
||||
page.value = 'attendance';
|
||||
}
|
||||
|
||||
loading.value = true;
|
||||
localCompareMembers.value = inProps.compareMembers;
|
||||
|
||||
const defaults = getLocalPageDefaults(page.value);
|
||||
selectedColumnFilter.value = defaults?.filteredColumn || '';
|
||||
selectedColumnOptions.value = defaults?.filteredValue ?? [];
|
||||
|
||||
setNewFilter(selectedColumnFilter.value, ...selectedColumnOptions.value);
|
||||
appApi
|
||||
.post('database/open', { dbPath: databaseName.value, create: true })
|
||||
.then(() => {
|
||||
@@ -320,6 +385,29 @@ function openUploadDialog() {
|
||||
uploadDialog.value?.open();
|
||||
}
|
||||
|
||||
function setColumnOptions(columnName: string) {
|
||||
const values = allMembers.value
|
||||
.map((e) => e[columnName as keyof Member]) // could be undefined
|
||||
.filter((v): v is string | number => v !== null && v !== undefined)
|
||||
.map((v) => String(v));
|
||||
|
||||
const selection = [...new Set(values)];
|
||||
|
||||
// Add special option for missing/null/empty values
|
||||
if (allMembers.value.some((e) => !e[columnName as keyof Member])) {
|
||||
selection.unshift('None');
|
||||
}
|
||||
|
||||
return selection;
|
||||
}
|
||||
|
||||
async function filterMembers(field: string, ...keys: string[]) {
|
||||
setNewFilter(field, ...keys);
|
||||
console.log(66, page.value);
|
||||
setLocalPageDefaults(page.value, field, keys);
|
||||
await updateMembers();
|
||||
}
|
||||
|
||||
//remove member from database
|
||||
function removeMember(...removeMembers: Members) {
|
||||
const memberIds: number[] = [];
|
||||
@@ -391,6 +479,7 @@ async function addMemberTo() {
|
||||
}
|
||||
emit('update-event');
|
||||
}
|
||||
|
||||
async function updateMemberLastVisit(members: Members) {
|
||||
const now = new Date();
|
||||
|
||||
@@ -416,6 +505,10 @@ async function updateMemberLastVisit(members: Members) {
|
||||
}
|
||||
})
|
||||
.catch((err) => NotifyResponse(err, 'error'));
|
||||
await updateMembers(localCompareMembers.value, inProps.addResponsible)
|
||||
.then(() => localCompareMembers.value?.push(...members))
|
||||
.catch((err) => NotifyResponse(err, 'error'));
|
||||
emit('update-event');
|
||||
}
|
||||
</script>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user