10 Commits

Author SHA1 Message Date
Adrian Zürcher
afff7c123a fix not updating member table close #30 2025-11-17 16:59:23 +01:00
Adrian Zürcher
94dc125a1b add bage to show attendance amount close #29 2025-11-17 16:58:58 +01:00
Adrian Zürcher
f070a4b0df fix filter output 2025-11-17 16:57:52 +01:00
Adrian Zürcher
260767680b remove unused code 2025-11-17 16:57:37 +01:00
Adrian Zürcher
09299c65de add new component searchable select close #28 2025-11-17 16:57:13 +01:00
Adrian Zürcher
39e5479947 fix wrong filter parameter 2025-11-16 08:42:54 +01:00
Adrian Zürcher
5c15fb5599 change padding to userfriendlier size on phone close #23 2025-11-16 08:42:28 +01:00
Adrian Zürcher
dce451bfc2 add new page header and decrease marging 2025-11-16 08:40:42 +01:00
Adrian Zürcher
bba4486136 fix translation 2025-11-16 08:39:31 +01:00
Adrian Zürcher
a18b01450d fix wrong input field to select clos #25 2025-11-16 08:39:17 +01:00
22 changed files with 615 additions and 427 deletions

View File

@@ -86,9 +86,9 @@ members: Mitglider
attendanceTable: Anweseheits Tabelle attendanceTable: Anweseheits Tabelle
excursionTable: Usflugs Tabelle excursionTable: Usflugs Tabelle
updated: aktualisiert updated: aktualisiert
events: Veranstalige events: Verastautige
eventNameIsRequired: Verastatigsname isch erforderlich eventNameIsRequired: Verastautigssname isch erforderlich
eventName: Verastatigsname eventName: Verastautigssname
attendees: Teilnähmer attendees: Teilnähmer
now: Jetzt now: Jetzt
addToEvent: Füge zu Veranstautig addToEvent: Füge zu Veranstautig

View File

@@ -1,7 +1,9 @@
<template> <template>
<DialogFrame ref="dialog" :header-title="localTitle"> <DialogFrame ref="dialog" :header-title="localTitle">
<div class="row justify-center"> <div class="row justify-center">
<q-select <SearchableSelect
class="q-mx-xl"
dense
autofocus autofocus
:label="$t('event')" :label="$t('event')"
filled filled
@@ -12,7 +14,7 @@
@keyup.enter="addAttendees" @keyup.enter="addAttendees"
map-options map-options
emit-value emit-value
></q-select> ></SearchableSelect>
</div> </div>
<div class="row justify-center"> <div class="row justify-center">
<q-btn class="q-ma-md" color="primary" no-caps @click="addAttendees">{{ localTitle }}</q-btn> <q-btn class="q-ma-md" color="primary" no-caps @click="addAttendees">{{ localTitle }}</q-btn>
@@ -40,6 +42,7 @@ import type { Members } from 'src/vueLib/models/member';
import EditAllDialog from 'src/components/EventEditAllDialog.vue'; import EditAllDialog from 'src/components/EventEditAllDialog.vue';
import { useAttendeesTable } from 'src/vueLib/tables/attendees/AttendeesTable'; import { useAttendeesTable } from 'src/vueLib/tables/attendees/AttendeesTable';
import { useEventTable } from 'src/vueLib/tables/events/EventsTable'; import { useEventTable } from 'src/vueLib/tables/events/EventsTable';
import SearchableSelect from 'src/vueLib/general/SearchableSelect .vue';
const dialog = ref(); const dialog = ref();
const newEventRef = ref(); const newEventRef = ref();
@@ -105,7 +108,7 @@ async function addAttendees() {
NotifyResponse(err, 'error'); NotifyResponse(err, 'error');
}); });
await updateAttendees(); await updateAttendees(0);
updateEvents(); updateEvents();
} }

View File

@@ -60,12 +60,17 @@
filled filled
v-model="localMember.email" v-model="localMember.email"
></q-input> ></q-input>
<q-input <q-select
class="q-ml-md col-5" class="q-ml-md col-5"
:label="$t('group')" :label="$t('group')"
:options="props.group"
filled filled
emit-value
map-options
option-value="name"
option-label="name"
v-model="localMember.group" v-model="localMember.group"
></q-input> ></q-select>
<q-select <q-select
class="q-ml-md col-5" class="q-ml-md col-5"
:label="$t('responsible')" :label="$t('responsible')"
@@ -118,6 +123,9 @@ const props = defineProps({
responsibles: { responsibles: {
type: Object as PropType<Members>, type: Object as PropType<Members>,
}, },
group: {
type: Array,
},
}); });
const emit = defineEmits(['update']); const emit = defineEmits(['update']);

View File

@@ -1,11 +1,11 @@
<template> <template>
<q-page> <q-page>
<h4 class="text-primary text-bold text-center">{{ $t('events') }}</h4> <SiteTitle :title="$t('events')" />
<EventsTable /> <EventsTable />
</q-page> </q-page>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import SiteTitle from 'src/vueLib/general/SiteTitle.vue';
import EventsTable from 'src/vueLib/tables/events/EventsTable.vue'; import EventsTable from 'src/vueLib/tables/events/EventsTable.vue';
</script> </script>

View File

@@ -1,10 +1,11 @@
<template> <template>
<q-page> <q-page>
<h4 class="text-primary text-bold text-center">{{ $t('groups') }}</h4> <SiteTitle :title="$t('groups')" />
<GroupTable /> <GroupTable />
</q-page> </q-page>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import SiteTitle from 'src/vueLib/general/SiteTitle.vue';
import GroupTable from 'src/vueLib/tables/group/GroupTable.vue'; import GroupTable from 'src/vueLib/tables/group/GroupTable.vue';
</script> </script>

View File

@@ -1,10 +1,13 @@
<template> <template>
<q-page> <q-page>
<h4 class="text-primary text-bold text-center">{{ $t('members') }}</h4> <SiteTitle :title="$t('members')" />
<MembersTable /> <div class="q-pa-md">
<MembersTable />
</div>
</q-page> </q-page>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import SiteTitle from 'src/vueLib/general/SiteTitle.vue';
import MembersTable from 'src/vueLib/tables/members/MembersTable.vue'; import MembersTable from 'src/vueLib/tables/members/MembersTable.vue';
</script> </script>

View File

@@ -1,11 +1,11 @@
<template> <template>
<q-page> <q-page>
<h4 class="text-primary text-bold text-center">{{ $t('responsibles') }}</h4> <SiteTitle :title="$t('responsibles')" />
<ResponsibleTable /> <ResponsibleTable />
</q-page> </q-page>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import SiteTitle from 'src/vueLib/general/SiteTitle.vue';
import ResponsibleTable from 'src/vueLib/tables/responsible/ResponsibleTable.vue'; import ResponsibleTable from 'src/vueLib/tables/responsible/ResponsibleTable.vue';
</script> </script>

View File

@@ -1,6 +1,5 @@
<template> <template>
<h4 class="text-primary text-bold text-center">{{ $t('userSettings') }}</h4> <SiteTitle :title="$t('userSettings')" />
<div class="text-h2 flex flex-center"> <div class="text-h2 flex flex-center">
<q-card class="q-gutter-md"> <q-card class="q-gutter-md">
<div> <div>
@@ -152,6 +151,7 @@ import { useNotify } from 'src/vueLib/general/useNotify';
import { type Settings } from 'src/vueLib/models/settings'; import { type Settings } from 'src/vueLib/models/settings';
import { useUserStore } from 'src/vueLib/login/userStore'; import { useUserStore } from 'src/vueLib/login/userStore';
import { setLocalSettings } from 'src/localstorage/localStorage'; import { setLocalSettings } from 'src/localstorage/localStorage';
import SiteTitle from 'src/vueLib/general/SiteTitle.vue';
const { NotifyResponse } = useNotify(); const { NotifyResponse } = useNotify();
const colorGroup = ref(false); const colorGroup = ref(false);

View File

@@ -1,6 +1,5 @@
<template> <template>
<h4 class="text-primary text-bold text-center">{{ $t('userSettings') }}</h4> <SiteTitle :title="$t('userSettings')" />
<div class="q-pa-md"> <div class="q-pa-md">
<div class="q-gutter-y-md"> <div class="q-gutter-y-md">
<q-card> <q-card>
@@ -35,6 +34,7 @@
import { ref } from 'vue'; import { ref } from 'vue';
import UserTable from 'src/vueLib/tables/users/UserTable.vue'; import UserTable from 'src/vueLib/tables/users/UserTable.vue';
import RoleTable from 'src/vueLib/tables/roles/RoleTable.vue'; import RoleTable from 'src/vueLib/tables/roles/RoleTable.vue';
import SiteTitle from 'src/vueLib/general/SiteTitle.vue';
const tab = ref('users'); const tab = ref('users');
</script> </script>

View File

@@ -25,12 +25,12 @@ const routes: RouteRecordRaw[] = [
}, },
{ {
path: 'responsible', path: 'responsible',
component: () => import('pages/ResponsibleTable.vue'), component: () => import('src/pages/ResponsibleTable.vue'),
meta: { requiresAuth: true, requiresAdmin: true }, meta: { requiresAuth: true, requiresAdmin: true },
}, },
{ {
path: 'group', path: 'group',
component: () => import('pages/GroupTable.vue'), component: () => import('src/pages/GroupTable.vue'),
meta: { requiresAuth: true, requiresAdmin: true }, meta: { requiresAuth: true, requiresAdmin: true },
}, },
{ {

View File

@@ -25,7 +25,7 @@
<q-separator color="black" /> <q-separator color="black" />
<!-- Content Slot --> <!-- Content Slot -->
<div class="scrollArea"> <div class="scrollArea" :style="'padding: ' + props.innerPadding + 'px'">
<slot /> <slot />
</div> </div>
@@ -48,6 +48,7 @@ const props = defineProps({
headerTitle: { type: String, default: '' }, headerTitle: { type: String, default: '' },
width: { type: Number, default: 400 }, width: { type: Number, default: 400 },
height: { type: Number, default: 250 }, height: { type: Number, default: 250 },
innerPadding: { type: Number, default: 16 },
}); });
// Fullscreen toggle // Fullscreen toggle
@@ -153,7 +154,6 @@ const cardStyle = computed(() => {
flex: 1 1 auto; flex: 1 1 auto;
min-height: 0; min-height: 0;
overflow-y: auto; overflow-y: auto;
padding: 16px;
} }
/* Resize handle in bottom right */ /* Resize handle in bottom right */

View File

@@ -0,0 +1,115 @@
<template>
<q-select
ref="selectRef"
v-model="modelValueLocal"
:options="filteredOptions"
:option-label="optionLabel"
:option-value="optionValue"
:use-input="search"
input-debounce="200"
@filter="filterFn"
@update:model-value="emitValue"
v-bind="$attrs"
><template v-slot:append
><q-btn
size="xs"
flat
dense
round
icon="search"
:color="search ? 'primary' : 'grey'"
@click="searchBox"
>
</q-btn
></template>
</q-select>
</template>
<script setup lang="ts" generic="T extends Record<string, string | number | object>">
import type { PropType } from 'vue';
import { ref, watch } from 'vue';
const search = ref(false);
const selectRef = ref();
defineOptions({ inheritAttrs: false });
const props = defineProps({
modelValue: {
type: [String, Number, Object] as PropType<string | number | object | null>,
default: null,
},
options: {
type: Array as PropType<T[]>,
required: true,
},
optionLabel: {
type: [Function, String] as PropType<((option: T) => string) | string>,
required: true,
},
optionValue: {
type: String,
required: true,
},
});
const emit = defineEmits<{
(e: 'update:modelValue', value: string | number | null): void;
}>();
const optionLabel = props.optionLabel;
const optionValue = props.optionValue;
const modelValueLocal = ref<string | number | object | null | undefined>(props.modelValue);
const filteredOptions = ref<T[]>([...props.options]);
function searchBox() {
if (search.value) {
selectRef.value.updateInputValue('');
}
search.value = !search.value;
}
watch(
() => props.modelValue,
(v) => (modelValueLocal.value = v),
);
watch(
() => props.options,
(o) => (filteredOptions.value = [...o]),
);
function filterFn(val: string, update: (fn: () => void) => void) {
update(() => {
if (!val) {
filteredOptions.value = [...props.options];
return;
}
const needle = val.toLowerCase();
filteredOptions.value = props.options.filter((opt) => {
let field = null;
if (typeof optionLabel === 'function') {
field = optionLabel(opt);
} else {
field = opt[optionLabel];
}
if (typeof field !== 'string' && typeof field !== 'number') return false;
return String(field).toLowerCase().includes(needle);
});
// Remove duplicates by optionValue
const seen = new Set();
filteredOptions.value = filteredOptions.value.filter((opt) => {
const value = opt[optionValue];
if (seen.has(value)) return false;
seen.add(value);
return true;
});
});
}
function emitValue(val: string | number | null) {
emit('update:modelValue', val);
}
</script>

View File

@@ -0,0 +1,12 @@
<template>
<h4 class="text-primary text-bold text-center" :style="{ margin: props.marging + 'px' }">
{{ props.title }}
</h4>
</template>
<script setup lang="ts">
const props = defineProps({
title: { type: String },
marging: { type: Number, default: 28 },
});
</script>

View File

@@ -38,7 +38,7 @@ export function useAttendeesTable() {
const loading = ref(false); const loading = ref(false);
//updates Attendees list from database //updates Attendees list from database
async function updateAttendees() { async function updateAttendees(eventArray: number) {
loading.value = true; loading.value = true;
let events: Events | undefined; let events: Events | undefined;
@@ -60,11 +60,16 @@ export function useAttendeesTable() {
loading.value = false; loading.value = false;
}); });
if (!events || events.length === 0 || !events[0]?.attendees || events[0].attendees === null) { if (
!events ||
events.length === 0 ||
!events[eventArray]?.attendees ||
events[eventArray].attendees === null
) {
attendees.value = []; attendees.value = [];
return; return;
} }
attendees.value = events[0].attendees ?? []; attendees.value = events[eventArray].attendees ?? [];
} }
return { return {

View File

@@ -1,102 +1,104 @@
<template> <template>
<div class="q-pa-md"> <q-table
<q-table flat
flat bordered
bordered ref="tableRef"
ref="tableRef" :title="$t('attendees')"
:title="$t('attendees')" title-class="text-bold text-blue-9"
title-class="text-bold text-blue-9" :no-data-label="$t('noDataAvailable')"
:no-data-label="$t('noDataAvailable')" :loading-label="$t('loading')"
:loading-label="$t('loading')" :rows-per-page-label="$t('recordsPerPage')"
:rows-per-page-label="$t('recordsPerPage')" :selected-rows-label="(val) => val + ' ' + $t('recordSelected')"
:selected-rows-label="(val) => val + ' ' + $t('recordSelected')" :rows="attendees"
:rows="attendees" :columns="columns"
:columns="columns" row-key="id"
row-key="id" v-model:pagination="pagination"
v-model:pagination="pagination" :loading="loading"
:loading="loading" :filter="filter"
:filter="filter" :selection="selectOption ? 'multiple' : 'none'"
:selection="selectOption ? 'multiple' : 'none'" v-model:selected="selected"
v-model:selected="selected" binary-state-sort
binary-state-sort dense
class="bigger-table-text" class="bigger-table-text"
> >
<template v-slot:top-left> <!--top left of event table-->
<q-btn-group push flat style="color: grey"> <template v-slot:top-left>
<q-btn <q-btn-group push flat style="color: grey">
v-if="user.isPermittedTo('events', 'write')" <q-btn
dense v-if="user.isPermittedTo('events', 'write')"
flat dense
icon="person" flat
@click="openAllValueDialog" icon="person"
><q-badge floating transparent color="primary" text-color="primary-text">+</q-badge> @click="openAttendanceDialog"
<q-tooltip>{{ $t('addNewAttendees') }}</q-tooltip> ><q-badge floating transparent color="primary" text-color="primary-text">+</q-badge>
</q-btn> <q-tooltip>{{ $t('addNewAttendees') }}</q-tooltip>
<q-btn </q-btn>
v-if="user.isPermittedTo('events', 'write')" <q-btn
dense v-if="user.isPermittedTo('events', 'write')"
flat dense
style="color: grey" flat
:icon="selectOption ? 'check_box' : 'check_box_outline_blank'" style="color: grey"
@click="selectOption = !selectOption" :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-menu>
</q-btn> </div>
</q-btn-group> <div v-if="selectOption && selected.length > 0" class="q-ml-md text-weight-bold">
<div v-if="selectOption && selected.length > 0"> {{ $t('selected') }}: {{ selected.length }}
<q-btn flat dense icon="more_vert" @click="openSubmenu = true" /> </div>
<q-menu v-if="openSubmenu" anchor="bottom middle" self="top middle"> </template>
<q-item <!--top right of event table-->
clickable <template v-slot:top-right>
v-close-popup <q-input filled dense debounce="300" v-model="filter" :placeholder="$t('search')">
@click="openRemoveDialog(...selected)" <template v-slot:append>
class="text-negative" <q-icon name="search" />
>{{ $t('delete') }}</q-item </template>
> </q-input>
</q-menu> </template>
</div> <!--body of event table-->
<div v-if="selectOption && selected.length > 0" class="q-ml-md text-weight-bold"> <template v-slot:body-cell="props">
{{ $t('selected') }}: {{ selected.length }} <q-td v-if="props.col.field === 'attendees'" :props="props">
</div> <q-btn v-if="props.value !== null && props.value.length > 0" dense flat icon="people"
</template> ><q-badge color="primary" text-color="primary-text" floating transparent>{{
<template v-slot:top-right> props.row.count
<q-input filled dense debounce="300" v-model="filter" :placeholder="$t('search')"> }}</q-badge></q-btn
<template v-slot:append> >
<q-icon name="search" /> </q-td>
</template> <q-td v-else :props="props">
</q-input> {{ props.value }}
</template> </q-td>
<template v-slot:body-cell="props"> </template>
<q-td v-if="props.col.field === 'attendees'" :props="props"> <template v-slot:body-cell-option="props">
<q-btn v-if="props.value !== null && props.value.length > 0" dense flat icon="people" <q-td :props="props">
><q-badge color="primary" text-color="primary-text" floating transparent>{{ <q-btn
props.row.count v-if="user.isPermittedTo('events', 'delete')"
}}</q-badge></q-btn flat
> dense
</q-td> @click="openRemoveDialog(props.row)"
<q-td v-else :props="props"> color="negative"
{{ props.value }} icon="delete"
</q-td> />
</template> </q-td>
<template v-slot:body-cell-option="props"> </template>
<q-td :props="props"> </q-table>
<q-btn <DialogFrame ref="attendanceDialog" :header-title="$t('members')" :width="700" :height="500">
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 <MembersTable
add-attendees add-attendees
:compare-members="attendees" :compare-members="attendees"
v-on:update-event="updateTable" v-on:update-event="(val) => updateTable(val)"
:event-id="localEvent?.id ?? 0" :event-id="localEvent?.id ?? 0"
/> />
</DialogFrame> </DialogFrame>
@@ -115,6 +117,7 @@
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
//imports
import { appApi } from 'src/boot/axios'; import { appApi } from 'src/boot/axios';
import { onMounted, type PropType, ref } from 'vue'; import { onMounted, type PropType, ref } from 'vue';
import type { Members } from 'src/vueLib/models/member'; import type { Members } from 'src/vueLib/models/member';
@@ -127,21 +130,24 @@ import type { Event } from 'src/vueLib/models/event';
import MembersTable from '../members/MembersTable.vue'; import MembersTable from '../members/MembersTable.vue';
import { i18n } from 'src/boot/lang'; import { i18n } from 'src/boot/lang';
export interface AttendeesDialog { //use constants and function of imports
getSelected: () => Members; const { attendees, pagination, loading, columns, updateAttendees } = useAttendeesTable();
}
//define given properties
const props = defineProps({ const props = defineProps({
event: { event: {
type: Object as PropType<Event>, type: Object as PropType<Event>,
required: true, required: true,
}, },
eventArray: { type: Number, default: 0, required: true },
}); });
// define emits
const emit = defineEmits(['update']); const emit = defineEmits(['update']);
// definitions
const { NotifyResponse } = useNotify(); const { NotifyResponse } = useNotify();
const memberTableDialog = ref(); const attendanceDialog = ref();
const okDialog = ref(); const okDialog = ref();
const deleteText = ref(''); const deleteText = ref('');
const selectOption = ref(false); const selectOption = ref(false);
@@ -151,16 +157,15 @@ const openSubmenu = ref(false);
const filter = ref(''); const filter = ref('');
const user = useUserStore(); const user = useUserStore();
const { attendees, pagination, loading, columns, updateAttendees } = useAttendeesTable(); // load at page mount
onMounted(() => { onMounted(() => {
localEvent.value = props.event; localEvent.value = props.event;
attendees.value = props.event.attendees ?? []; attendees.value = props.event.attendees ?? [];
}); });
//opens dialog for one value //opens attendance dialog
function openAllValueDialog() { function openAttendanceDialog() {
memberTableDialog.value?.open(); attendanceDialog.value?.open();
} }
//opens remove dialog //opens remove dialog
@@ -174,10 +179,11 @@ function openRemoveDialog(...attendees: Members) {
} else { } else {
deleteText.value = String(attendees.length) + ' ' + i18n.global.t('attendees'); deleteText.value = String(attendees.length) + ' ' + i18n.global.t('attendees');
} }
okDialog.value?.open(attendees); okDialog.value?.open(attendees);
} }
//remove Attendees from database //remove attendees from database
async function removeAttendees(...removeAttendees: Members) { async function removeAttendees(...removeAttendees: Members) {
if (!localEvent.value) { if (!localEvent.value) {
NotifyResponse('event is empty', 'error'); NotifyResponse('event is empty', 'error');
@@ -200,27 +206,17 @@ async function removeAttendees(...removeAttendees: Members) {
.finally(() => { .finally(() => {
loading.value = false; loading.value = false;
}); });
await updateAttendees(); await updateAttendees(props.eventArray);
emit('update'); emit('update', -removeAttendees.length);
} }
async function updateTable() { async function updateTable(amount: number) {
await updateAttendees(); await updateAttendees(props.eventArray);
emit('update'); emit('update', amount);
} }
</script> </script>
<style> <style>
@keyframes blink-yellow {
0%,
100% {
background-color: yellow;
}
50% {
background-color: transparent;
}
}
.bigger-table-text .q-table__middle td { .bigger-table-text .q-table__middle td {
font-size: 14px; font-size: 14px;
} }

View File

@@ -1,6 +1,13 @@
<template> <template>
<DialogFrame ref="dialog" :header-title="$t('attendees')" :width="700" :height="600"> <DialogFrame
ref="dialog"
:header-title="$t('attendees')"
:width="700"
:height="600"
:inner-padding="0"
>
<q-card> <q-card>
<!-- tabs declaration in header-->
<q-tabs <q-tabs
v-model="tab" v-model="tab"
dense dense
@@ -10,21 +17,37 @@
align="justify" align="justify"
narrow-indicator narrow-indicator
> >
<q-tab no-caps name="attendance" :label="$t('attendees')" /> <q-tab no-caps name="attendance" :label="$t('attendees')"
<q-tab no-caps name="noneAttendees" :label="$t('noneAttendees')" /> ><q-badge color="primary" text-color="primary-text" floating transparent>{{
attendees.length
}}</q-badge></q-tab
>
<q-tab no-caps name="noneAttendees" :label="$t('noneAttendees')"
><q-badge color="primary" text-color="primary-text" floating transparent>{{
missingAttendanceAmount
}}</q-badge></q-tab
>
</q-tabs> </q-tabs>
<q-separator /> <q-separator />
<!--tab of attenting members-->
<q-tab-panels v-model="tab" animated> <q-tab-panels v-model="tab" animated>
<q-tab-panel name="attendance"> <q-tab-panel name="attendance" style="padding: 0px">
<AttendeesTable :event="localEvent!" v-on:update="updateTable" /> <AttendeesTable
:event="localEvent!"
:event-array="eA"
v-on:update="(val) => updateTable(val)"
/>
</q-tab-panel> </q-tab-panel>
<q-tab-panel name="noneAttendees"> <!--tab of missing attenting members-->
<q-tab-panel name="noneAttendees" style="padding: 0px">
<MembersTable <MembersTable
add-attendees add-attendees
:compare-members="attendees" :compare-members="attendees"
v-on:update-event="updateTable" v-on:update-event="(val) => updateTable(val)"
:event-id="localEvent?.id ?? 0" :event-id="localEvent?.id ?? 0"
/> />
</q-tab-panel> </q-tab-panel>
@@ -34,28 +57,68 @@
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
// imports
import AttendeesTable from './AttendeesTable.vue'; import AttendeesTable from './AttendeesTable.vue';
import MembersTable from '../members/MembersTable.vue'; import MembersTable from '../members/MembersTable.vue';
import DialogFrame from 'src/vueLib/dialog/DialogFrame.vue'; import DialogFrame from 'src/vueLib/dialog/DialogFrame.vue';
import type { Event } from 'src/vueLib/models/event'; import type { Event } from 'src/vueLib/models/event';
import { ref } from 'vue'; import { ref } from 'vue';
import { useAttendeesTable } from './AttendeesTable'; import { useAttendeesTable } from './AttendeesTable';
import { useMemberTable } from '../members/MembersTable';
import { getLocalPageDefaults } from 'src/localstorage/localStorage';
//use constants and function of imports
const { attendees, updateAttendees } = useAttendeesTable();
const { setNewFilter, filteredMembers, updateMembers } = useMemberTable();
// declare emits
const emit = defineEmits(['update']); const emit = defineEmits(['update']);
// declarations
const dialog = ref(); const dialog = ref();
const localEvent = ref<Event>(); const localEvent = ref<Event>();
const missingAttendanceAmount = ref(0);
const attendanceAmount = ref(0);
const eA = ref(0);
const tab = ref('attendance'); const tab = ref('attendance');
const { attendees, updateAttendees } = useAttendeesTable();
const open = (event: Event) => { // opens attendace dialog of event
const open = async (eventArray: number, event: Event) => {
// local declarations
localEvent.value = event; localEvent.value = event;
attendees.value = event.attendees ?? []; attendanceAmount.value = event.attendees.length ?? [];
eA.value = eventArray;
// get attendance
await updateAttendees(eventArray);
//get missing attendance from memer updateTable
await updateMembers(event.attendees);
// set custom filter
const defaults = getLocalPageDefaults('attendance');
setNewFilter(defaults?.filteredColumn || '', ...(defaults?.filteredValue ?? []));
// set amount of missing attendace
missingAttendanceAmount.value = filteredMembers.value.length;
//open atttendance dialog
dialog.value.open(); dialog.value.open();
}; };
async function updateTable() { //updates amount of table display and both tables
await updateAttendees(); async function updateTable(amount?: number) {
// check wether amount is given and if it adds or substracts amount
if (amount && amount < 0) {
missingAttendanceAmount.value -= amount;
} else if (amount) {
missingAttendanceAmount.value = amount;
}
// update attendace table
await updateAttendees(eA.value);
// update for amoutn display in tab
if (localEvent.value) localEvent.value.attendees = attendees.value;
//emit to update event table
emit('update'); emit('update');
} }

View File

@@ -19,6 +19,7 @@
:selection="selectOption ? 'multiple' : 'none'" :selection="selectOption ? 'multiple' : 'none'"
v-model:selected="selected" v-model:selected="selected"
binary-state-sort binary-state-sort
dense
class="bigger-table-text" class="bigger-table-text"
> >
<template v-slot:top-left> <template v-slot:top-left>
@@ -68,7 +69,7 @@
</template> </template>
<template v-slot:body-cell="props"> <template v-slot:body-cell="props">
<q-td v-if="props.col.field === 'attendees'" :props="props"> <q-td v-if="props.col.field === 'attendees'" :props="props">
<q-btn dense flat icon="people" @click="openAttendees(props.row)" <q-btn dense flat icon="people" @click="openAttendees(props.rowIndex, props.row)"
><q-badge color="primary" text-color="primary-text" floating transparent>{{ ><q-badge color="primary" text-color="primary-text" floating transparent>{{
props.row.count props.row.count
}}</q-badge></q-btn }}</q-badge></q-btn
@@ -217,8 +218,8 @@ function openRemoveDialog(...Events: Events) {
okDialog.value?.open(Events); okDialog.value?.open(Events);
} }
function openAttendees(attendees: Members | null) { function openAttendees(eventArray: number, attendees: Members | null) {
attendeesDialog.value.open(attendees); attendeesDialog.value.open(eventArray, attendees);
} }
//remove Event from database //remove Event from database
@@ -243,20 +244,6 @@ function removeEvent(...removeEvents: Events) {
</script> </script>
<style> <style>
@keyframes blink-yellow {
0%,
100% {
background-color: yellow;
}
50% {
background-color: transparent;
}
}
.blink-yellow {
animation: blink-yellow 1.5s step-start 6 !important;
}
.bigger-table-text .q-table__middle td { .bigger-table-text .q-table__middle td {
font-size: 14px; font-size: 14px;
} }

View File

@@ -19,6 +19,7 @@
:selection="selectOption ? 'multiple' : 'none'" :selection="selectOption ? 'multiple' : 'none'"
v-model:selected="selected" v-model:selected="selected"
binary-state-sort binary-state-sort
dense
class="bigger-table-text" class="bigger-table-text"
> >
<template v-slot:top-left> <template v-slot:top-left>
@@ -245,16 +246,6 @@ async function save() {
</script> </script>
<style> <style>
@keyframes blink-yellow {
0%,
100% {
background-color: yellow;
}
50% {
background-color: transparent;
}
}
.bigger-table-text .q-table__middle td { .bigger-table-text .q-table__middle td {
font-size: 14px; font-size: 14px;
} }

View File

@@ -241,8 +241,9 @@ export function useMemberTable() {
.finally(() => { .finally(() => {
loading.value = false; loading.value = false;
//filter same members out so list is shorter //filter same members out so list is shorter
filteredMembers.value = allMembers.value;
if (filter) { if (filter) {
filteredMembers.value = allMembers.value.filter( filteredMembers.value = filteredMembers.value.filter(
(m1) => (m1) =>
!filter.some((m2) => { !filter.some((m2) => {
if (filterbyName) { if (filterbyName) {
@@ -256,7 +257,7 @@ export function useMemberTable() {
//second filter //second filter
const list = filterList.value ?? []; const list = filterList.value ?? [];
if (filterList.value && filterList.value.length > 0) { if (filterList.value && filterList.value.length > 0) {
filteredMembers.value = allMembers.value.filter((member) => filteredMembers.value = filteredMembers.value.filter((member) =>
list.every((filterItem) => { list.every((filterItem) => {
const keys = filterItem.keys ?? []; const keys = filterItem.keys ?? [];
if (keys.includes('null')) return true; if (keys.includes('null')) return true;
@@ -278,7 +279,6 @@ export function useMemberTable() {
); );
return; return;
} }
filteredMembers.value = allMembers.value;
}); });
} }

View File

@@ -1,224 +1,234 @@
<template> <template>
<div class="q-pa-md"> <q-table
<q-table flat
flat bordered
bordered ref="tableRef"
ref="tableRef" title="Members"
title="Members" title-class="text-bold text-blue-9"
title-class="text-bold text-blue-9" :no-data-label="$t('noDataAvailable')"
:no-data-label="$t('noDataAvailable')" :loading-label="$t('loading')"
:loading-label="$t('loading')" :rows-per-page-label="$t('recordsPerPage')"
:rows-per-page-label="$t('recordsPerPage')" :selected-rows-label="(val) => val + ' ' + $t('recordSelected')"
:selected-rows-label="(val) => val + ' ' + $t('recordSelected')" :rows="filteredMembers"
:rows="filteredMembers" :columns="columns"
:columns="columns" row-key="id"
row-key="id" v-model:pagination="pagination"
v-model:pagination="pagination" :loading="loading"
:loading="loading" :filter="filter"
:filter="filter" :selection="selectOption ? 'multiple' : 'none'"
:selection="selectOption ? 'multiple' : 'none'" v-model:selected="selected"
v-model:selected="selected" binary-state-sort
binary-state-sort dense
class="bigger-table-text" class="bigger-table-text"
> >
<template v-slot:top-left> <!-- top left of table-->
<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-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"> <template v-slot:top-left>
<q-item clickable v-close-popup @click="addToEvent" class="text-primary">{{ <div>
$t('addToEvent') <q-btn-group push flat style="color: grey">
}}</q-item> <q-btn
<q-item v-if="user.isPermittedTo('members', 'write')"
v-if="user.isPermittedTo('members', 'delete')" dense
clickable flat
v-close-popup icon="add"
@click="openRemoveDialog(...selected)" @click="openAllValueDialog(null)"
class="text-negative" >
>{{ $t('delete') }}</q-item <q-tooltip>{{ $t('addNewMember') }}</q-tooltip>
> </q-btn>
</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 }}
</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"
:class="getRowClass(props.row)"
:style="user.isPermittedTo('members', 'write') ? 'cursor: pointer' : ''"
@click="
user.isPermittedTo('members', 'write') &&
openSingleValueDialog(props.col.label, props.col.name, props.row)
"
>
{{ props.value }}
</q-td>
</template>
<template v-slot:body-cell-cake="props">
<q-td :props="props" :class="getRowClass(props.row)">
<q-icon
:name="isXDaysBeforeAnnualDate(props.row.birthday, 1) ? 'cake' : ''"
:color="'red'"
size="md"
/>
</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
v-if="responsibles.length > 0"
:readonly="!user.isPermittedTo('members', 'write')"
:options="responsibles"
:option-label="(opt) => opt.firstName + ' ' + opt.lastName"
v-model="props.row.responsiblePerson"
@update:model-value="updateMember(props.row)"
></q-select>
</q-td>
</template>
<template v-slot:body-cell-option="props">
<q-td :props="props">
<q-btn <q-btn
v-if="user.isPermittedTo('members', 'write') || user.isPermittedTo('members', 'delete')" v-if="user.isPermittedTo('members', 'write') || user.isPermittedTo('members', 'delete')"
flat
dense dense
icon="more_vert" flat
@click="openSubmenu = true" style="color: grey"
/> :icon="selectOption ? 'check_box' : 'check_box_outline_blank'"
<q-menu v-if="openSubmenu" anchor="top right" self="top left"> @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-tooltip>{{ $t('addToEvent') }}</q-tooltip>
</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 <q-item
v-if="user.isPermittedTo('members', 'write')" v-if="user.isPermittedTo('members', 'delete')"
clickable clickable
v-close-popup v-close-popup
@click="openAllValueDialog(props.row)" @click="openRemoveDialog(...selected)"
class="text-primary"
>{{ $t('edit') }}</q-item
>
<q-item
v-if="
user.isPermittedTo('members', 'delete') &&
!inProps.addAttendees &&
!inProps.addResponsible
"
clickable
v-close-popup
@click="openRemoveDialog(props.row)"
class="text-negative" class="text-negative"
title="zu"
>{{ $t('delete') }}</q-item >{{ $t('delete') }}</q-item
> >
</q-menu> </q-menu>
</q-td> </div>
</template> <q-card flat class="q-pa-sm">
</q-table> <q-select
</div> :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 }}
</div>
</template>
<!-- top right of table-->
<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>
<!-- table body content-->
<template v-slot:body-cell="props">
<q-td
:props="props"
:class="getRowClass(props.row)"
:style="user.isPermittedTo('members', 'write') ? 'cursor: pointer' : ''"
@click="
user.isPermittedTo('members', 'write') &&
openSingleValueDialog(props.col.label, props.col.name, props.row)
"
>
{{ props.value }}
</q-td>
</template>
<template v-slot:body-cell-cake="props">
<q-td :props="props" :class="getRowClass(props.row)">
<q-icon
:name="isXDaysBeforeAnnualDate(props.row.birthday, 1) ? 'cake' : ''"
:color="'red'"
size="md"
/>
</q-td>
</template>
<template v-slot:body-cell-group="props">
<q-td :props="props">
<SearchableSelect
v-if="groups.length > 0"
dense
: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-td>
</template>
<template v-slot:body-cell-responsiblePerson="props">
<q-td :props="props">
<SearchableSelect
v-if="responsibles.length > 0"
:readonly="!user.isPermittedTo('members', 'write')"
:options="responsibles"
:option-label="(opt) => opt.firstName + ' ' + opt.lastName"
option-value="firstName"
v-model="props.row.responsiblePerson"
@update:model-value="updateMember(props.row)"
/>
</q-td>
</template>
<template v-slot:body-cell-option="props">
<q-td :props="props">
<q-btn
v-if="user.isPermittedTo('members', 'write') || user.isPermittedTo('members', 'delete')"
flat
dense
icon="more_vert"
@click="openSubmenu = true"
/>
<q-menu v-if="openSubmenu" anchor="top right" self="top left">
<q-item
dense
v-if="user.isPermittedTo('members', 'write')"
clickable
v-close-popup
@click="openAllValueDialog(props.row)"
class="text-primary"
>{{ $t('edit') }}</q-item
>
<q-item
v-if="
user.isPermittedTo('members', 'delete') &&
!inProps.addAttendees &&
!inProps.addResponsible
"
dense
clickable
v-close-popup
@click="openRemoveDialog(props.row)"
class="text-negative"
title="zu"
>{{ $t('delete') }}</q-item
>
</q-menu>
</q-td>
</template>
</q-table>
<EditOneDialog <EditOneDialog
ref="editOneDialog" ref="editOneDialog"
endpoint="members/edit" endpoint="members/edit"
@@ -228,6 +238,7 @@
<EditAllDialog <EditAllDialog
ref="editAllDialog" ref="editAllDialog"
:responsibles="responsibles" :responsibles="responsibles"
:group="groups"
v-on:update="updateMember" v-on:update="updateMember"
></EditAllDialog> ></EditAllDialog>
<OkDialog <OkDialog
@@ -264,6 +275,7 @@ import { databaseName } from 'src/vueLib/models/settings';
import { useUserStore } from 'src/vueLib/login/userStore'; import { useUserStore } from 'src/vueLib/login/userStore';
import { i18n } from 'src/boot/lang'; import { i18n } from 'src/boot/lang';
import { getLocalPageDefaults, setLocalPageDefaults } from 'src/localstorage/localStorage'; import { getLocalPageDefaults, setLocalPageDefaults } from 'src/localstorage/localStorage';
import SearchableSelect from 'src/vueLib/general/SearchableSelect .vue';
const inProps = defineProps({ const inProps = defineProps({
addAttendees: { type: Boolean }, addAttendees: { type: Boolean },
@@ -338,13 +350,13 @@ onMounted(() => {
selectedColumnFilter.value = defaults?.filteredColumn || ''; selectedColumnFilter.value = defaults?.filteredColumn || '';
selectedColumnOptions.value = defaults?.filteredValue ?? []; selectedColumnOptions.value = defaults?.filteredValue ?? [];
// set custom filter
setNewFilter(selectedColumnFilter.value, ...selectedColumnOptions.value); setNewFilter(selectedColumnFilter.value, ...selectedColumnOptions.value);
appApi appApi
.post('database/open', { dbPath: databaseName.value, create: true }) .post('database/open', { dbPath: databaseName.value, create: true })
.then(() => { .then(() => {
updateMembers(inProps.compareMembers, inProps.addResponsible).catch((err) => updateTable().catch((err) => NotifyResponse(err, 'error'));
NotifyResponse(err, 'error'),
);
}) })
.catch((err) => NotifyResponse(err, 'error')) .catch((err) => NotifyResponse(err, 'error'))
@@ -353,6 +365,12 @@ onMounted(() => {
}); });
}); });
async function updateTable() {
await updateMembers(localCompareMembers.value, inProps.addResponsible).catch((err) =>
NotifyResponse(err, 'error'),
);
}
// opens dialog for all member values // opens dialog for all member values
function openSingleValueDialog(label: string, field: string, member: Member) { function openSingleValueDialog(label: string, field: string, member: Member) {
editOneDialog.value?.open(label, field, member); editOneDialog.value?.open(label, field, member);
@@ -404,7 +422,7 @@ function setColumnOptions(columnName: string) {
async function filterMembers(field: string, ...keys: string[]) { async function filterMembers(field: string, ...keys: string[]) {
setNewFilter(field, ...keys); setNewFilter(field, ...keys);
setLocalPageDefaults(page.value, field, keys); setLocalPageDefaults(page.value, field, keys);
await updateMembers(); await updateTable();
} }
//remove member from database //remove member from database
@@ -418,7 +436,7 @@ function removeMember(...removeMembers: Members) {
appApi appApi
.post('members/delete', { ids: memberIds }) .post('members/delete', { ids: memberIds })
.then(() => { .then(() => {
updateMembers().catch((err) => NotifyResponse(err, 'error')); updateTable().catch((err) => NotifyResponse(err, 'error'));
selected.value = []; selected.value = [];
}) })
.catch((err) => NotifyResponse(err, 'error')) .catch((err) => NotifyResponse(err, 'error'))
@@ -433,7 +451,7 @@ function updateMember(member: Member | null) {
.post('/members/edit', [member]) .post('/members/edit', [member])
.then(() => NotifyResponse(i18n.global.t('memberUpdated'))) .then(() => NotifyResponse(i18n.global.t('memberUpdated')))
.catch((err) => NotifyResponse(err, 'error')); .catch((err) => NotifyResponse(err, 'error'));
updateMembers().catch((err) => NotifyResponse(err, 'error')); updateTable().catch((err) => NotifyResponse(err, 'error'));
} }
function addToEvent() { function addToEvent() {
@@ -475,8 +493,10 @@ async function addMemberTo() {
if (inProps.addAttendees) { if (inProps.addAttendees) {
await updateMemberLastVisit(selected.value); await updateMemberLastVisit(selected.value);
} else {
await updateTable();
emit('update-event', filteredMembers.value.length);
} }
emit('update-event');
} }
async function updateMemberLastVisit(members: Members) { async function updateMemberLastVisit(members: Members) {
@@ -504,11 +524,13 @@ async function updateMemberLastVisit(members: Members) {
} }
}) })
.catch((err) => NotifyResponse(err, 'error')); .catch((err) => NotifyResponse(err, 'error'));
await updateMembers(localCompareMembers.value, inProps.addResponsible) localCompareMembers.value?.push(...members);
.then(() => localCompareMembers.value?.push(...members)) await updateTable().catch((err) => NotifyResponse(err, 'error'));
.catch((err) => NotifyResponse(err, 'error'));
emit('update-event'); emit('update-event', filteredMembers.value.length);
} }
defineExpose({ allMembers, filteredMembers });
</script> </script>
<style> <style>

View File

@@ -19,6 +19,7 @@
:selection="selectOption ? 'multiple' : 'none'" :selection="selectOption ? 'multiple' : 'none'"
v-model:selected="selected" v-model:selected="selected"
binary-state-sort binary-state-sort
dense
class="bigger-table-text" class="bigger-table-text"
> >
<template v-slot:top-left> <template v-slot:top-left>
@@ -220,16 +221,6 @@ function removeResponsible(...removeResponsibles: Members) {
</script> </script>
<style> <style>
@keyframes blink-yellow {
0%,
100% {
background-color: yellow;
}
50% {
background-color: transparent;
}
}
.bigger-table-text .q-table__middle td { .bigger-table-text .q-table__middle td {
font-size: 14px; font-size: 14px;
} }

View File

@@ -19,6 +19,7 @@
:selection="selectOption ? 'multiple' : 'none'" :selection="selectOption ? 'multiple' : 'none'"
v-model:selected="selected" v-model:selected="selected"
binary-state-sort binary-state-sort
dense
class="bigger-table-text" class="bigger-table-text"
> >
<template v-slot:top-left> <template v-slot:top-left>
@@ -232,16 +233,6 @@ function removeRole(...removeRoles: Roles) {
</script> </script>
<style> <style>
@keyframes blink-yellow {
0%,
100% {
background-color: yellow;
}
50% {
background-color: transparent;
}
}
.blink-yellow { .blink-yellow {
animation: blink-yellow 1.5s step-start 6 !important; animation: blink-yellow 1.5s step-start 6 !important;
} }