add new group table and filter for member table
This commit is contained in:
@@ -1,124 +1,122 @@
|
||||
<template>
|
||||
<DialogFrame ref="dialog" :header-title="$t('attendees')" :width="600" :height="600">
|
||||
<div class="q-pa-md">
|
||||
<q-table
|
||||
flat
|
||||
bordered
|
||||
ref="tableRef"
|
||||
:title="$t('attendees')"
|
||||
title-class="text-bold text-blue-9"
|
||||
:no-data-label="$t('noDataAvailable')"
|
||||
:loading-label="$t('loading')"
|
||||
:rows-per-page-label="$t('recordsPerPage')"
|
||||
:selected-rows-label="(val) => val + ' ' + $t('recordSelected')"
|
||||
:rows="attendees"
|
||||
:columns="columns"
|
||||
row-key="id"
|
||||
v-model:pagination="pagination"
|
||||
:loading="loading"
|
||||
:filter="filter"
|
||||
:selection="selectOption ? 'multiple' : 'none'"
|
||||
v-model:selected="selected"
|
||||
binary-state-sort
|
||||
class="bigger-table-text"
|
||||
>
|
||||
<template v-slot:top-left>
|
||||
<q-btn-group push flat style="color: grey">
|
||||
<q-btn
|
||||
v-if="user.isPermittedTo('events', 'write')"
|
||||
dense
|
||||
flat
|
||||
icon="person"
|
||||
@click="openAllValueDialog"
|
||||
><q-badge floating transparent color="primary" text-color="primary-text">+</q-badge>
|
||||
<q-tooltip>{{ $t('addNewAttendees') }}</q-tooltip>
|
||||
</q-btn>
|
||||
<q-btn
|
||||
v-if="user.isPermittedTo('events', 'write')"
|
||||
dense
|
||||
flat
|
||||
style="color: grey"
|
||||
:icon="selectOption ? 'check_box' : 'check_box_outline_blank'"
|
||||
@click="selectOption = !selectOption"
|
||||
<div class="q-pa-md">
|
||||
<q-table
|
||||
flat
|
||||
bordered
|
||||
ref="tableRef"
|
||||
:title="$t('attendees')"
|
||||
title-class="text-bold text-blue-9"
|
||||
:no-data-label="$t('noDataAvailable')"
|
||||
:loading-label="$t('loading')"
|
||||
:rows-per-page-label="$t('recordsPerPage')"
|
||||
:selected-rows-label="(val) => val + ' ' + $t('recordSelected')"
|
||||
:rows="attendees"
|
||||
:columns="columns"
|
||||
row-key="id"
|
||||
v-model:pagination="pagination"
|
||||
:loading="loading"
|
||||
:filter="filter"
|
||||
:selection="selectOption ? 'multiple' : 'none'"
|
||||
v-model:selected="selected"
|
||||
binary-state-sort
|
||||
class="bigger-table-text"
|
||||
>
|
||||
<template v-slot:top-left>
|
||||
<q-btn-group push flat style="color: grey">
|
||||
<q-btn
|
||||
v-if="user.isPermittedTo('events', 'write')"
|
||||
dense
|
||||
flat
|
||||
icon="person"
|
||||
@click="openAllValueDialog"
|
||||
><q-badge floating transparent color="primary" text-color="primary-text">+</q-badge>
|
||||
<q-tooltip>{{ $t('addNewAttendees') }}</q-tooltip>
|
||||
</q-btn>
|
||||
<q-btn
|
||||
v-if="user.isPermittedTo('events', 'write')"
|
||||
dense
|
||||
flat
|
||||
style="color: grey"
|
||||
:icon="selectOption ? 'check_box' : 'check_box_outline_blank'"
|
||||
@click="selectOption = !selectOption"
|
||||
>
|
||||
<q-tooltip>{{ $t('selectAttendeesOptions') }}</q-tooltip>
|
||||
</q-btn>
|
||||
</q-btn-group>
|
||||
<div v-if="selectOption && selected.length > 0">
|
||||
<q-btn 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="openRemoveDialog(...selected)"
|
||||
class="text-negative"
|
||||
>{{ $t('delete') }}</q-item
|
||||
>
|
||||
<q-tooltip>{{ $t('selectAttendeesOptions') }}</q-tooltip>
|
||||
</q-btn>
|
||||
</q-btn-group>
|
||||
<div v-if="selectOption && selected.length > 0">
|
||||
<q-btn 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="openRemoveDialog(...selected)"
|
||||
class="text-negative"
|
||||
>{{ $t('delete') }}</q-item
|
||||
>
|
||||
</q-menu>
|
||||
</div>
|
||||
<div v-if="selectOption && selected.length > 0" class="q-ml-md text-weight-bold">
|
||||
{{ $t('selected') }}: {{ selected.length }}
|
||||
</div>
|
||||
</template>
|
||||
<template v-slot:top-right>
|
||||
<q-input filled dense debounce="300" v-model="filter" :placeholder="$t('search')">
|
||||
<template v-slot:append>
|
||||
<q-icon name="search" />
|
||||
</template>
|
||||
</q-input>
|
||||
</template>
|
||||
<template v-slot:body-cell="props">
|
||||
<q-td v-if="props.col.field === 'attendees'" :props="props">
|
||||
<q-btn v-if="props.value !== null && props.value.length > 0" dense flat icon="people"
|
||||
><q-badge color="primary" text-color="primary-text" floating transparent>{{
|
||||
props.row.count
|
||||
}}</q-badge></q-btn
|
||||
>
|
||||
</q-td>
|
||||
<q-td v-else :props="props">
|
||||
{{ props.value }}
|
||||
</q-td>
|
||||
</template>
|
||||
<template v-slot:body-cell-option="props">
|
||||
<q-td :props="props">
|
||||
<q-btn
|
||||
v-if="user.isPermittedTo('events', 'delete')"
|
||||
flat
|
||||
dense
|
||||
@click="openRemoveDialog(props.row)"
|
||||
color="negative"
|
||||
icon="delete"
|
||||
/>
|
||||
</q-td>
|
||||
</template>
|
||||
</q-table>
|
||||
</div>
|
||||
<DialogFrame ref="memberTableDialog" :header-title="$t('members')" :width="700" :height="500">
|
||||
<MembersTable
|
||||
add-attendees
|
||||
:compare-members="attendees"
|
||||
v-on:update-event="updateTable"
|
||||
:event-id="localEvent?.id ?? 0"
|
||||
/>
|
||||
</DialogFrame>
|
||||
|
||||
<OkDialog
|
||||
ref="okDialog"
|
||||
:dialog-label="$t('delete')"
|
||||
:text="$t('doYouWantToDelete') + ' ' + deleteText"
|
||||
label-color="red"
|
||||
:button-cancel-label="$t('cancel')"
|
||||
:button-ok-label="$t('confirm')"
|
||||
:button-ok-flat="false"
|
||||
button-ok-color="red"
|
||||
v-on:update-confirm="(val) => removeAttendees(...val)"
|
||||
></OkDialog>
|
||||
</q-menu>
|
||||
</div>
|
||||
<div v-if="selectOption && selected.length > 0" class="q-ml-md text-weight-bold">
|
||||
{{ $t('selected') }}: {{ selected.length }}
|
||||
</div>
|
||||
</template>
|
||||
<template v-slot:top-right>
|
||||
<q-input filled dense debounce="300" v-model="filter" :placeholder="$t('search')">
|
||||
<template v-slot:append>
|
||||
<q-icon name="search" />
|
||||
</template>
|
||||
</q-input>
|
||||
</template>
|
||||
<template v-slot:body-cell="props">
|
||||
<q-td v-if="props.col.field === 'attendees'" :props="props">
|
||||
<q-btn v-if="props.value !== null && props.value.length > 0" dense flat icon="people"
|
||||
><q-badge color="primary" text-color="primary-text" floating transparent>{{
|
||||
props.row.count
|
||||
}}</q-badge></q-btn
|
||||
>
|
||||
</q-td>
|
||||
<q-td v-else :props="props">
|
||||
{{ props.value }}
|
||||
</q-td>
|
||||
</template>
|
||||
<template v-slot:body-cell-option="props">
|
||||
<q-td :props="props">
|
||||
<q-btn
|
||||
v-if="user.isPermittedTo('events', 'delete')"
|
||||
flat
|
||||
dense
|
||||
@click="openRemoveDialog(props.row)"
|
||||
color="negative"
|
||||
icon="delete"
|
||||
/>
|
||||
</q-td>
|
||||
</template>
|
||||
</q-table>
|
||||
</div>
|
||||
<DialogFrame ref="memberTableDialog" :header-title="$t('members')" :width="700" :height="500">
|
||||
<MembersTable
|
||||
add-attendees
|
||||
:compare-members="attendees"
|
||||
v-on:update-event="updateTable"
|
||||
:event-id="localEvent?.id ?? 0"
|
||||
/>
|
||||
</DialogFrame>
|
||||
|
||||
<OkDialog
|
||||
ref="okDialog"
|
||||
:dialog-label="$t('delete')"
|
||||
:text="$t('doYouWantToDelete') + ' ' + deleteText"
|
||||
label-color="red"
|
||||
:button-cancel-label="$t('cancel')"
|
||||
:button-ok-label="$t('confirm')"
|
||||
:button-ok-flat="false"
|
||||
button-ok-color="red"
|
||||
v-on:update-confirm="(val) => removeAttendees(...val)"
|
||||
></OkDialog>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { appApi } from 'src/boot/axios';
|
||||
import { ref } from 'vue';
|
||||
import { onMounted, type PropType, ref } from 'vue';
|
||||
import type { Members } from 'src/vueLib/models/member';
|
||||
import DialogFrame from 'src/vueLib/dialog/DialogFrame.vue';
|
||||
import OkDialog from 'src/components/dialog/OkDialog.vue';
|
||||
@@ -133,11 +131,17 @@ export interface AttendeesDialog {
|
||||
getSelected: () => Members;
|
||||
}
|
||||
|
||||
const props = defineProps({
|
||||
event: {
|
||||
type: Object as PropType<Event>,
|
||||
required: true,
|
||||
},
|
||||
});
|
||||
|
||||
const emit = defineEmits(['update']);
|
||||
|
||||
const { NotifyResponse } = useNotify();
|
||||
const memberTableDialog = ref();
|
||||
const dialog = ref();
|
||||
const okDialog = ref();
|
||||
const deleteText = ref('');
|
||||
const selectOption = ref(false);
|
||||
@@ -149,11 +153,10 @@ const user = useUserStore();
|
||||
|
||||
const { attendees, pagination, loading, columns, updateAttendees } = useAttendeesTable();
|
||||
|
||||
const open = (event: Event) => {
|
||||
localEvent.value = event;
|
||||
attendees.value = event.attendees ?? [];
|
||||
dialog.value.open();
|
||||
};
|
||||
onMounted(() => {
|
||||
localEvent.value = props.event;
|
||||
attendees.value = props.event.attendees ?? [];
|
||||
});
|
||||
|
||||
//opens dialog for one value
|
||||
function openAllValueDialog() {
|
||||
@@ -177,7 +180,7 @@ function openRemoveDialog(...attendees: Members) {
|
||||
//remove Attendees from database
|
||||
async function removeAttendees(...removeAttendees: Members) {
|
||||
if (!localEvent.value) {
|
||||
console.error('event is empty');
|
||||
NotifyResponse('event is empty', 'error');
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -197,18 +200,14 @@ async function removeAttendees(...removeAttendees: Members) {
|
||||
.finally(() => {
|
||||
loading.value = false;
|
||||
});
|
||||
emit('update');
|
||||
await updateAttendees();
|
||||
emit('update');
|
||||
}
|
||||
|
||||
async function updateTable() {
|
||||
await updateAttendees();
|
||||
emit('update');
|
||||
}
|
||||
|
||||
defineExpose({
|
||||
open,
|
||||
});
|
||||
</script>
|
||||
|
||||
<style>
|
||||
|
||||
63
src/vueLib/tables/attendees/AttendeesTableDialog.vue
Normal file
63
src/vueLib/tables/attendees/AttendeesTableDialog.vue
Normal file
@@ -0,0 +1,63 @@
|
||||
<template>
|
||||
<DialogFrame ref="dialog" :header-title="$t('attendees')" :width="700" :height="600">
|
||||
<q-card>
|
||||
<q-tabs
|
||||
v-model="tab"
|
||||
dense
|
||||
class="text-grey"
|
||||
active-color="primary"
|
||||
indicator-color="primary"
|
||||
align="justify"
|
||||
narrow-indicator
|
||||
>
|
||||
<q-tab no-caps name="attendance" :label="$t('attendees')" />
|
||||
<q-tab no-caps name="noneAttendees" :label="$t('noneAttendees')" />
|
||||
</q-tabs>
|
||||
|
||||
<q-separator />
|
||||
|
||||
<q-tab-panels v-model="tab" animated>
|
||||
<q-tab-panel name="attendance">
|
||||
<AttendeesTable :event="localEvent!" v-on:update="updateTable" />
|
||||
</q-tab-panel>
|
||||
<q-tab-panel name="noneAttendees">
|
||||
<MembersTable
|
||||
add-attendees
|
||||
:compare-members="attendees"
|
||||
v-on:update-event="updateTable"
|
||||
:event-id="localEvent?.id ?? 0"
|
||||
/>
|
||||
</q-tab-panel>
|
||||
</q-tab-panels>
|
||||
</q-card>
|
||||
</DialogFrame>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import AttendeesTable from './AttendeesTable.vue';
|
||||
import MembersTable from '../members/MembersTable.vue';
|
||||
import DialogFrame from 'src/vueLib/dialog/DialogFrame.vue';
|
||||
import type { Event } from 'src/vueLib/models/event';
|
||||
import { ref } from 'vue';
|
||||
import { useAttendeesTable } from './AttendeesTable';
|
||||
|
||||
const emit = defineEmits(['update']);
|
||||
|
||||
const dialog = ref();
|
||||
const localEvent = ref<Event>();
|
||||
const tab = ref('attendance');
|
||||
const { attendees, updateAttendees } = useAttendeesTable();
|
||||
|
||||
const open = (event: Event) => {
|
||||
localEvent.value = event;
|
||||
attendees.value = event.attendees ?? [];
|
||||
dialog.value.open();
|
||||
};
|
||||
|
||||
async function updateTable() {
|
||||
await updateAttendees();
|
||||
emit('update');
|
||||
}
|
||||
|
||||
defineExpose({ open });
|
||||
</script>
|
||||
@@ -130,7 +130,7 @@
|
||||
v-on:update="updateEvents"
|
||||
></EditOneDialog>
|
||||
<EditAllDialog ref="editAllDialog" v-on:update="updateEvents"></EditAllDialog>
|
||||
<AttendeesTable ref="attendeesDialog" v-on:update="updateEvents" />
|
||||
<AttendeesTableDialog ref="attendeesDialog" v-on:update="updateEvents" />
|
||||
<OkDialog
|
||||
ref="okDialog"
|
||||
:dialog-label="$t('delete')"
|
||||
@@ -155,7 +155,7 @@ import { useNotify } from 'src/vueLib/general/useNotify';
|
||||
import { useEventTable } from './EventsTable';
|
||||
import { databaseName } from 'src/vueLib/models/settings';
|
||||
import { useUserStore } from 'src/vueLib/login/userStore';
|
||||
import AttendeesTable from '../attendees/AttendeesTable.vue';
|
||||
import AttendeesTableDialog from '../attendees/AttendeesTableDialog.vue';
|
||||
import type { Members } from 'src/vueLib/models/member';
|
||||
import { i18n } from 'src/boot/lang';
|
||||
|
||||
|
||||
61
src/vueLib/tables/group/GroupTable.ts
Normal file
61
src/vueLib/tables/group/GroupTable.ts
Normal file
@@ -0,0 +1,61 @@
|
||||
import { appApi } from 'src/boot/axios';
|
||||
import { ref, computed } from 'vue';
|
||||
import type { Members } from 'src/vueLib/models/member';
|
||||
import { useNotify } from 'src/vueLib/general/useNotify';
|
||||
import { i18n } from 'boot/lang';
|
||||
|
||||
export function useGroupTable() {
|
||||
const groups = ref<Members>([]);
|
||||
|
||||
const pagination = ref({
|
||||
sortBy: 'firstName',
|
||||
descending: false,
|
||||
page: 1,
|
||||
rowsPerPage: 20,
|
||||
});
|
||||
|
||||
const columns = computed(() => [
|
||||
{
|
||||
name: 'name',
|
||||
align: 'left' as const,
|
||||
label: i18n.global.t('name'),
|
||||
field: 'name',
|
||||
sortable: true,
|
||||
},
|
||||
{ name: 'option', align: 'center' as const, label: '', field: 'option', icon: 'option' },
|
||||
]);
|
||||
|
||||
const { NotifyResponse } = useNotify();
|
||||
|
||||
const loading = ref(false);
|
||||
|
||||
//updates group list from database
|
||||
async function updateGroups() {
|
||||
loading.value = true;
|
||||
|
||||
await appApi
|
||||
.get('groups')
|
||||
.then((resp) => {
|
||||
if (resp.data === null) {
|
||||
groups.value = [];
|
||||
return;
|
||||
}
|
||||
groups.value = resp.data as Members;
|
||||
})
|
||||
|
||||
.catch((err) => {
|
||||
NotifyResponse(err, 'error');
|
||||
})
|
||||
.finally(() => {
|
||||
loading.value = false;
|
||||
});
|
||||
}
|
||||
|
||||
return {
|
||||
groups,
|
||||
pagination,
|
||||
columns,
|
||||
loading,
|
||||
updateGroups,
|
||||
};
|
||||
}
|
||||
267
src/vueLib/tables/group/GroupTable.vue
Normal file
267
src/vueLib/tables/group/GroupTable.vue
Normal file
@@ -0,0 +1,267 @@
|
||||
<template>
|
||||
<div class="q-pa-md">
|
||||
<q-table
|
||||
flat
|
||||
bordered
|
||||
ref="tableRef"
|
||||
title="groups"
|
||||
title-class="text-bold text-blue-9"
|
||||
:no-data-label="$t('noDataAvailable')"
|
||||
:loading-label="$t('loading')"
|
||||
:rows-per-page-label="$t('recordsPerPage')"
|
||||
:selected-rows-label="(val) => val + ' ' + $t('recordSelected')"
|
||||
:rows="groups"
|
||||
:columns="columns"
|
||||
row-key="id"
|
||||
v-model:pagination="pagination"
|
||||
:loading="loading"
|
||||
:filter="filter"
|
||||
:selection="selectOption ? 'multiple' : 'none'"
|
||||
v-model:selected="selected"
|
||||
binary-state-sort
|
||||
class="bigger-table-text"
|
||||
>
|
||||
<template v-slot:top-left>
|
||||
<q-btn-group push flat style="color: grey">
|
||||
<q-btn
|
||||
v-if="user.isPermittedTo('group', 'write')"
|
||||
dense
|
||||
flat
|
||||
icon="add"
|
||||
@click="openGroupDialog()"
|
||||
>
|
||||
<q-tooltip>{{ $t('addNewgroup') }}</q-tooltip>
|
||||
</q-btn>
|
||||
<q-btn
|
||||
v-if="user.isPermittedTo('group', 'write') || user.isPermittedTo('group', 'delete')"
|
||||
dense
|
||||
flat
|
||||
style="color: grey"
|
||||
:icon="selectOption ? 'check_box' : 'check_box_outline_blank'"
|
||||
@click="selectOption = !selectOption"
|
||||
>
|
||||
<q-tooltip>{{ $t('selectgroupOptions') }}</q-tooltip>
|
||||
</q-btn>
|
||||
</q-btn-group>
|
||||
<div v-if="selectOption && selected.length > 0">
|
||||
<q-btn flat dense icon="more_vert" @click="openSubmenu = true" />
|
||||
<q-menu v-if="openSubmenu" anchor="bottom middle" self="top middle">
|
||||
<q-item
|
||||
v-if="user.isPermittedTo('group', 'delete')"
|
||||
clickable
|
||||
v-close-popup
|
||||
@click="openRemoveDialog(...selected)"
|
||||
class="text-negative"
|
||||
>{{ $t('delete') }}</q-item
|
||||
>
|
||||
</q-menu>
|
||||
</div>
|
||||
<div v-if="selectOption && selected.length > 0" class="text-weight-bold">
|
||||
{{ $t('selected') }}: {{ selected.length }}
|
||||
</div>
|
||||
</template>
|
||||
<template v-slot:top-right>
|
||||
<q-input filled dense debounce="300" v-model="filter" :placeholder="$t('search')">
|
||||
<template v-slot:append>
|
||||
<q-icon name="search" />
|
||||
</template>
|
||||
</q-input>
|
||||
</template>
|
||||
<template v-slot:body-cell="props">
|
||||
<q-td
|
||||
:props="props"
|
||||
:style="user.isPermittedTo('group', 'write') ? 'cursor: pointer' : ''"
|
||||
@click="
|
||||
user.isPermittedTo('group', 'write') && openGroupDialog(props.col.label, props.row)
|
||||
"
|
||||
>
|
||||
{{ props.value }}
|
||||
</q-td>
|
||||
</template>
|
||||
<template v-slot:body-cell-option="props">
|
||||
<q-td :props="props">
|
||||
<q-btn
|
||||
v-if="user.isPermittedTo('group', 'write') || user.isPermittedTo('group', 'delete')"
|
||||
flat
|
||||
dense
|
||||
icon="more_vert"
|
||||
@click="openSubmenu = true"
|
||||
/>
|
||||
<q-menu v-if="openSubmenu" anchor="top right" self="top left">
|
||||
<q-item
|
||||
v-if="user.isPermittedTo('group', 'delete')"
|
||||
clickable
|
||||
v-close-popup
|
||||
@click="openRemoveDialog(props.row)"
|
||||
class="text-negative"
|
||||
title="zu"
|
||||
>{{ $t('delete') }}</q-item
|
||||
>
|
||||
</q-menu>
|
||||
</q-td>
|
||||
</template>
|
||||
</q-table>
|
||||
</div>
|
||||
<DialogFrame ref="groupDialog" :header-title="$t('addNewgroup')" :height="600" :width="500">
|
||||
<q-form ref="form">
|
||||
<div class="row justify-center q-gutter-md">
|
||||
<q-input
|
||||
class="q-ml-md col-5 required"
|
||||
:label="$t('groupName')"
|
||||
filled
|
||||
:rules="[(val) => !!val || $t('groupNameIsRequired')]"
|
||||
v-model="localGroup.name"
|
||||
autofocus
|
||||
@keyup.enter="save()"
|
||||
></q-input>
|
||||
</div>
|
||||
</q-form>
|
||||
<div class="row justify-center">
|
||||
<q-btn class="q-ma-md" color="primary" no-caps @click="save()">{{ $t('save') }}</q-btn>
|
||||
</div>
|
||||
</DialogFrame>
|
||||
<OkDialog
|
||||
ref="okDialog"
|
||||
:dialog-label="$t('delete')"
|
||||
:text="$t('doYouWantToDelete') + ' ' + deleteText"
|
||||
label-color="red"
|
||||
:button-cancel-label="$t('cancel')"
|
||||
:button-ok-label="$t('confirm')"
|
||||
:button-ok-flat="false"
|
||||
button-ok-color="red"
|
||||
v-on:update-confirm="(val) => removegroup(...val)"
|
||||
></OkDialog>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { appApi } from 'src/boot/axios';
|
||||
import { ref, onMounted } from 'vue';
|
||||
import DialogFrame from 'src/vueLib/dialog/DialogFrame.vue';
|
||||
import OkDialog from 'src/components/dialog/OkDialog.vue';
|
||||
import { useNotify } from 'src/vueLib/general/useNotify';
|
||||
import { useGroupTable } from './GroupTable';
|
||||
import { databaseName } from 'src/vueLib/models/settings';
|
||||
import { useUserStore } from 'src/vueLib/login/userStore';
|
||||
import { i18n } from 'src/boot/lang';
|
||||
import type { Group, Groups } from 'src/vueLib/models/group';
|
||||
|
||||
export interface groupDialog {
|
||||
getSelected: () => Groups;
|
||||
}
|
||||
|
||||
const { NotifyResponse } = useNotify();
|
||||
const groupDialog = ref();
|
||||
const form = ref();
|
||||
const localGroup = ref<Group>({} as Group);
|
||||
const localLabel = ref('');
|
||||
const okDialog = ref();
|
||||
const deleteText = ref('');
|
||||
const selectOption = ref(false);
|
||||
const selected = ref<Groups>([]);
|
||||
const openSubmenu = ref(false);
|
||||
const filter = ref('');
|
||||
const user = useUserStore();
|
||||
|
||||
const { groups, pagination, loading, columns, updateGroups } = useGroupTable();
|
||||
|
||||
//load on mounting page
|
||||
onMounted(() => {
|
||||
loading.value = true;
|
||||
|
||||
appApi
|
||||
.post('database/open', { dbPath: databaseName.value, create: true })
|
||||
.then(() => {
|
||||
updateGroups().catch((err) => {
|
||||
NotifyResponse(err, 'error');
|
||||
});
|
||||
})
|
||||
.catch((err) => NotifyResponse(err, 'error'))
|
||||
|
||||
.finally(() => {
|
||||
loading.value = false;
|
||||
});
|
||||
});
|
||||
|
||||
//opens dialog for one value
|
||||
function openGroupDialog(label?: string, group?: Group) {
|
||||
localLabel.value = label!;
|
||||
localGroup.value = group ? group : <Group>{ name: '' };
|
||||
groupDialog.value?.open();
|
||||
}
|
||||
|
||||
//opens remove dialog
|
||||
function openRemoveDialog(...groups: Groups) {
|
||||
if (groups.length === 1) {
|
||||
deleteText.value = "'" + localGroup.value.name + "''";
|
||||
} else {
|
||||
deleteText.value = String(groups.length) + ' ' + i18n.global.t('groups');
|
||||
}
|
||||
okDialog.value?.open(groups);
|
||||
}
|
||||
|
||||
//remove group from database
|
||||
function removegroup(...removegroups: Groups) {
|
||||
const groupIds: number[] = [];
|
||||
|
||||
removegroups.forEach((group: Group) => {
|
||||
groupIds.push(group.id);
|
||||
});
|
||||
|
||||
appApi
|
||||
.post('groups/delete', { ids: groupIds })
|
||||
.then(() => {
|
||||
updateGroups().catch((err) => {
|
||||
NotifyResponse(err, 'error');
|
||||
});
|
||||
selected.value = [];
|
||||
})
|
||||
.catch((err) => NotifyResponse(err, 'error'))
|
||||
.finally(() => {
|
||||
loading.value = false;
|
||||
});
|
||||
}
|
||||
|
||||
async function save() {
|
||||
const valid = await form.value.validate();
|
||||
|
||||
if (!valid) return;
|
||||
|
||||
let query = 'groups/edit';
|
||||
let payload = JSON.stringify([localGroup.value]);
|
||||
|
||||
if (!localGroup.value.id) {
|
||||
query = 'groups/add';
|
||||
payload = JSON.stringify(localGroup.value);
|
||||
}
|
||||
|
||||
appApi
|
||||
.post(query, payload)
|
||||
.then(() => {
|
||||
updateGroups().catch((err) => NotifyResponse(err, 'error'));
|
||||
groupDialog.value.close();
|
||||
})
|
||||
.catch((err) => NotifyResponse(err, 'error'));
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
@keyframes blink-yellow {
|
||||
0%,
|
||||
100% {
|
||||
background-color: yellow;
|
||||
}
|
||||
50% {
|
||||
background-color: transparent;
|
||||
}
|
||||
}
|
||||
|
||||
.bigger-table-text .q-table__middle td {
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.bigger-table-text .q-table__top,
|
||||
.bigger-table-text .q-table__bottom,
|
||||
.bigger-table-text th {
|
||||
font-size: 14px;
|
||||
}
|
||||
</style>
|
||||
@@ -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