From 44f355a5ea01d1d291cf5d2943fd811d6d5da6cf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adrian=20Z=C3=BCrcher?= Date: Sat, 15 Nov 2025 14:17:30 +0100 Subject: [PATCH] add new group table and filter for member table --- backend/go.mod | 4 +- backend/go.sum | 8 +- backend/main.go | 6 + src/assets/lang/de-CH.yaml | 10 + src/assets/lang/de-DE.yaml | 10 + src/assets/lang/en-US.yaml | 12 +- src/layouts/MainLayout.vue | 10 + src/pages/GroupTable.vue | 10 + src/router/routes.ts | 5 + src/vueLib/checkboxes/permissions.ts | 5 + src/vueLib/models/group.ts | 6 + .../tables/attendees/AttendeesTable.vue | 251 ++++++++-------- .../tables/attendees/AttendeesTableDialog.vue | 63 +++++ src/vueLib/tables/events/EventsTable.vue | 4 +- src/vueLib/tables/group/GroupTable.ts | 61 ++++ src/vueLib/tables/group/GroupTable.vue | 267 ++++++++++++++++++ src/vueLib/tables/members/MembersTable.ts | 70 ++++- src/vueLib/tables/members/MembersTable.vue | 227 ++++++++++----- src/vueLib/utils/utils.ts | 12 + 19 files changed, 828 insertions(+), 213 deletions(-) create mode 100644 src/pages/GroupTable.vue create mode 100644 src/vueLib/models/group.ts create mode 100644 src/vueLib/tables/attendees/AttendeesTableDialog.vue create mode 100644 src/vueLib/tables/group/GroupTable.ts create mode 100644 src/vueLib/tables/group/GroupTable.vue create mode 100644 src/vueLib/utils/utils.ts diff --git a/backend/go.mod b/backend/go.mod index 47d3d4f..addd446 100644 --- a/backend/go.mod +++ b/backend/go.mod @@ -3,8 +3,8 @@ module backend go 1.24.5 require ( - gitea.tecamino.com/paadi/access-handler v1.0.24 - gitea.tecamino.com/paadi/memberDB v1.1.2 + gitea.tecamino.com/paadi/access-handler v1.0.25 + gitea.tecamino.com/paadi/memberDB v1.1.3 gitea.tecamino.com/paadi/tecamino-dbm v0.1.1 gitea.tecamino.com/paadi/tecamino-logger v0.2.1 github.com/gin-contrib/cors v1.7.6 diff --git a/backend/go.sum b/backend/go.sum index 214ef07..dce7756 100644 --- a/backend/go.sum +++ b/backend/go.sum @@ -1,9 +1,9 @@ -gitea.tecamino.com/paadi/access-handler v1.0.24 h1:wgCMJg1tgtPSCCVWLyNx1bT3B1N2NwHth0UJAH2nZIY= -gitea.tecamino.com/paadi/access-handler v1.0.24/go.mod h1:wKsB5/Rvaj580gdg3+GbUf5V/0N00XN6cID+C/8135M= +gitea.tecamino.com/paadi/access-handler v1.0.25 h1:GiMnkEM0/fo2B1uCzGVyjpAhM2S58LG22N6+BdtdpgQ= +gitea.tecamino.com/paadi/access-handler v1.0.25/go.mod h1:wKsB5/Rvaj580gdg3+GbUf5V/0N00XN6cID+C/8135M= gitea.tecamino.com/paadi/dbHandler v1.0.8 h1:ZWSBM/KFtLwTv2cBqwK1mOxWAxAfL0BcWEC3kJ9JALU= gitea.tecamino.com/paadi/dbHandler v1.0.8/go.mod h1:y/xn/POJg1DO++67uKvnO23lJQgh+XFQq7HZCS9Getw= -gitea.tecamino.com/paadi/memberDB v1.1.2 h1:j/Tsr7JnzAkdOvgjG77TzTVBWd4vBrmEFzPXNpW7GYk= -gitea.tecamino.com/paadi/memberDB v1.1.2/go.mod h1:/Af2OeJIHq+8kE5L5DlJxhSJjB75eWBcKRpkxi+n9bU= +gitea.tecamino.com/paadi/memberDB v1.1.3 h1:ZwSA+TNL1ZvL8bMnJ5a2odc44bQBa31gVxD2fBA6o0I= +gitea.tecamino.com/paadi/memberDB v1.1.3/go.mod h1:/Af2OeJIHq+8kE5L5DlJxhSJjB75eWBcKRpkxi+n9bU= gitea.tecamino.com/paadi/tecamino-dbm v0.1.1 h1:vAq7mwUxlxJuLzCQSDMrZCwo8ky5usWi9Qz+UP+WnkI= gitea.tecamino.com/paadi/tecamino-dbm v0.1.1/go.mod h1:+tmf1rjPaKEoNeUcr1vdtoFIFweNG3aUGevDAl3NMBk= gitea.tecamino.com/paadi/tecamino-logger v0.2.1 h1:sQTBKYPdzn9mmWX2JXZBtGBvNQH7cuXIwsl4TD0aMgE= diff --git a/backend/main.go b/backend/main.go index 342b567..384f2f3 100644 --- a/backend/main.go +++ b/backend/main.go @@ -138,6 +138,7 @@ func main() { role := auth.Group("", accessHandler.AuthorizeRole("/api")) role.GET("/members", dbHandler.GetMember) auth.GET("/events", dbHandler.GetEvent) + auth.GET("/groups", dbHandler.GetGroup) auth.GET("/users", accessHandler.GetUser) auth.GET("/roles", accessHandler.GetRole) @@ -154,6 +155,10 @@ func main() { auth.POST("/events/delete/attendees", dbHandler.DeleteAttendee) auth.POST("/events/delete", dbHandler.DeleteEvent) + auth.POST("/groups/add", dbHandler.NewGroup) + auth.POST("/groups/edit", dbHandler.UpdateGroup) + auth.POST("/groups/delete", dbHandler.DeleteGroup) + auth.GET("/responsible", dbHandler.GetResponsible) auth.POST("/responsible/add", dbHandler.AddNewResponsible) auth.POST("/responsible/delete", dbHandler.DeleteResponsible) @@ -164,6 +169,7 @@ func main() { auth.POST("/users/add", accessHandler.AddUser) auth.POST("/users/update", accessHandler.UpdateUser) + auth.POST("/users/new/password", accessHandler.ChangePassword) auth.POST("/users/delete", accessHandler.DeleteUser) api.POST("/login/refresh", accessHandler.Refresh) diff --git a/src/assets/lang/de-CH.yaml b/src/assets/lang/de-CH.yaml index befcb39..e426c8c 100644 --- a/src/assets/lang/de-CH.yaml +++ b/src/assets/lang/de-CH.yaml @@ -4,6 +4,7 @@ lastName: Nachname birthday: Geburtstag email: Email group: Gruppe +groups: Gruppen age: Auter address: Adresse town: Ort @@ -121,3 +122,12 @@ dark_mode: Dunkel-Modus light_mode: Hell-Modus import: Import export: Export +changePassword: Passwort ändere +noneAttendees: Fählendi Telnähmer +addNewgroup: Neui Gruppe +selectgroupOptions: Wähle Gruppe Optionen +groupNameIsRequired: Gruppename isch erforderlich +groupName: Gruppename +filterByColumn: Spaltenfilter +filterByColumnValue: Spaltenwerte +saveAsDefault: Aus Standard spichere diff --git a/src/assets/lang/de-DE.yaml b/src/assets/lang/de-DE.yaml index f66e307..9291844 100644 --- a/src/assets/lang/de-DE.yaml +++ b/src/assets/lang/de-DE.yaml @@ -4,6 +4,7 @@ lastName: Nachname birthday: Geburtstag email: Email group: Gruppe +groups: Gruppen age: Alter address: Adresse town: Ort @@ -121,3 +122,12 @@ dark_mode: Dunkel-Modus light_mode: Hell-Modus import: Import export: Export +changePassword: Passwort ändern +noneAttendees: Fehlende Teilnehmer +addNewgroup: Neue Gruppe +selectgroupOptions: Wähle Gruppen Optionen +groupNameIsRequired: Gruppenname ist erforderlich +groupName: Gruppenname +filterByColumn: Spaltenfilter +filterByColumnValue: Spaltenwerte +saveAsDefault: Als Standard speichern diff --git a/src/assets/lang/en-US.yaml b/src/assets/lang/en-US.yaml index 4baf7d4..81e956b 100644 --- a/src/assets/lang/en-US.yaml +++ b/src/assets/lang/en-US.yaml @@ -4,6 +4,7 @@ lastName: Name birthday: Birthday email: Email group: Group +groups: Groups age: Age address: Address town: Town @@ -12,7 +13,7 @@ phone: Phone responsible: Responsible firstVisit: First Visit lastVisit: Last Visit -search: search +search: Search noDataAvailable: No data available importCSV: Import CSV exportCSV: Export CSV @@ -121,3 +122,12 @@ dark_mode: Dark-Mode light_mode: Light-Mode import: Import export: Export +changePassword: change Password +noneAttendees: Missing Attendees +addNewgroup: New Group +selectgroupOptions: Select Group Options +groupNameIsRequired: Groupname is required +groupName: Groupname +filterByColumn: Columnfilter +filterByColumnValue: Columnvalues +saveAsDefault: Save a Default diff --git a/src/layouts/MainLayout.vue b/src/layouts/MainLayout.vue index 745bafa..bf28454 100644 --- a/src/layouts/MainLayout.vue +++ b/src/layouts/MainLayout.vue @@ -53,6 +53,16 @@ > {{ $t('responsible') }} + + {{ $t('groups') }} + diff --git a/src/pages/GroupTable.vue b/src/pages/GroupTable.vue new file mode 100644 index 0000000..311d350 --- /dev/null +++ b/src/pages/GroupTable.vue @@ -0,0 +1,10 @@ + + + diff --git a/src/router/routes.ts b/src/router/routes.ts index 0710150..5711876 100644 --- a/src/router/routes.ts +++ b/src/router/routes.ts @@ -28,6 +28,11 @@ const routes: RouteRecordRaw[] = [ component: () => import('pages/ResponsibleTable.vue'), meta: { requiresAuth: true, requiresAdmin: true }, }, + { + path: 'group', + component: () => import('pages/GroupTable.vue'), + meta: { requiresAuth: true, requiresAdmin: true }, + }, { path: 'settings', component: () => import('pages/SettingsPage.vue'), diff --git a/src/vueLib/checkboxes/permissions.ts b/src/vueLib/checkboxes/permissions.ts index d2857e6..1de87fa 100644 --- a/src/vueLib/checkboxes/permissions.ts +++ b/src/vueLib/checkboxes/permissions.ts @@ -35,6 +35,11 @@ export const defaultPermissions = [ label: i18n.global.t('responsible'), permission: 0, }, + { + name: 'group', + label: i18n.global.t('group'), + permission: 0, + }, { name: 'excursionTable', label: i18n.global.t('excursionTable'), diff --git a/src/vueLib/models/group.ts b/src/vueLib/models/group.ts new file mode 100644 index 0000000..a14a8b8 --- /dev/null +++ b/src/vueLib/models/group.ts @@ -0,0 +1,6 @@ +export interface Group { + id: number; + name: string; +} + +export type Groups = Group[]; diff --git a/src/vueLib/tables/attendees/AttendeesTable.vue b/src/vueLib/tables/attendees/AttendeesTable.vue index 0206815..c385063 100644 --- a/src/vueLib/tables/attendees/AttendeesTable.vue +++ b/src/vueLib/tables/attendees/AttendeesTable.vue @@ -1,124 +1,122 @@ diff --git a/src/vueLib/tables/members/MembersTable.ts b/src/vueLib/tables/members/MembersTable.ts index 0c5074f..c8ae96f 100644 --- a/src/vueLib/tables/members/MembersTable.ts +++ b/src/vueLib/tables/members/MembersTable.ts @@ -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([]); + const allMembers = ref([]); + const filteredMembers = ref([]); + 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, diff --git a/src/vueLib/tables/members/MembersTable.vue b/src/vueLib/tables/members/MembersTable.vue index 48733a2..dcc5caf 100644 --- a/src/vueLib/tables/members/MembersTable.vue +++ b/src/vueLib/tables/members/MembersTable.vue @@ -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" > +