From fb27e9c0260eb69c94784083e437ae92a6005a07 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adrian=20Z=C3=BCrcher?= Date: Wed, 12 Nov 2025 17:17:43 +0100 Subject: [PATCH] add new export option with permissions close #13 --- backend/go.mod | 2 +- backend/go.sum | 4 +- src/assets/lang/de-CH.yaml | 3 ++ src/assets/lang/de-DE.yaml | 3 ++ src/assets/lang/en-US.yaml | 3 ++ src/components/RoleEditAllDialog.vue | 2 +- .../checkboxes/CheckBoxGroupPermissions.vue | 15 +++++++ src/vueLib/login/userStore.ts | 6 ++- src/vueLib/tables/members/MembersTable.ts | 43 +++++++++++++++++++ src/vueLib/tables/members/MembersTable.vue | 12 +++++- 10 files changed, 87 insertions(+), 6 deletions(-) diff --git a/backend/go.mod b/backend/go.mod index 6b12e18..47d3d4f 100644 --- a/backend/go.mod +++ b/backend/go.mod @@ -3,7 +3,7 @@ module backend go 1.24.5 require ( - gitea.tecamino.com/paadi/access-handler v1.0.23 + gitea.tecamino.com/paadi/access-handler v1.0.24 gitea.tecamino.com/paadi/memberDB v1.1.2 gitea.tecamino.com/paadi/tecamino-dbm v0.1.1 gitea.tecamino.com/paadi/tecamino-logger v0.2.1 diff --git a/backend/go.sum b/backend/go.sum index 388c273..214ef07 100644 --- a/backend/go.sum +++ b/backend/go.sum @@ -1,5 +1,5 @@ -gitea.tecamino.com/paadi/access-handler v1.0.23 h1:bsQgGEyiV8nutZbkNKCDkHWhIpOUEMV22wePxQsn3QU= -gitea.tecamino.com/paadi/access-handler v1.0.23/go.mod h1:wKsB5/Rvaj580gdg3+GbUf5V/0N00XN6cID+C/8135M= +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/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= diff --git a/src/assets/lang/de-CH.yaml b/src/assets/lang/de-CH.yaml index 263f0ba..befcb39 100644 --- a/src/assets/lang/de-CH.yaml +++ b/src/assets/lang/de-CH.yaml @@ -15,6 +15,7 @@ lastVisit: Letscht Bsuech search: Suechi noDataAvailable: Keni Date importCSV: importier CSV +exportCSV: exportier CSV selectMemberOptions: Wähle Mitglieder Optione addNewMember: Neues Mitglied csvOptions: CSV Optionen @@ -118,3 +119,5 @@ responsibles: Verantwortliche comment: Bemerkung dark_mode: Dunkel-Modus light_mode: Hell-Modus +import: Import +export: Export diff --git a/src/assets/lang/de-DE.yaml b/src/assets/lang/de-DE.yaml index 437fc8d..f66e307 100644 --- a/src/assets/lang/de-DE.yaml +++ b/src/assets/lang/de-DE.yaml @@ -15,6 +15,7 @@ lastVisit: Letzter Besuch search: Suche noDataAvailable: Keine Daten importCSV: importiere CSV +exportCSV: exportiere CSV selectMemberOptions: Wähle Mitglieder Optionen addNewMember: Neues Mitglied csvOptions: CSV Optionen @@ -118,3 +119,5 @@ responsibles: Verantwortliche comment: Bemerkung dark_mode: Dunkel-Modus light_mode: Hell-Modus +import: Import +export: Export diff --git a/src/assets/lang/en-US.yaml b/src/assets/lang/en-US.yaml index 0da10e6..4baf7d4 100644 --- a/src/assets/lang/en-US.yaml +++ b/src/assets/lang/en-US.yaml @@ -15,6 +15,7 @@ lastVisit: Last Visit search: search noDataAvailable: No data available importCSV: Import CSV +exportCSV: Export CSV selectMemberOptions: Select Member Options addNewMember: Add new Member csvOptions: CSV Options @@ -118,3 +119,5 @@ responsibles: Responsibles comment: Comment dark_mode: Dark-Mode light_mode: Light-Mode +import: Import +export: Export diff --git a/src/components/RoleEditAllDialog.vue b/src/components/RoleEditAllDialog.vue index 8ffd5cc..def3f8b 100644 --- a/src/components/RoleEditAllDialog.vue +++ b/src/components/RoleEditAllDialog.vue @@ -3,7 +3,7 @@ ref="dialog" :header-title="newRole ? $t('addNewRole') : 'Edit ' + localRole.role" :height="700" - :width="500" + :width="700" >
{{ i18n.global.t('delete') }} + {{ i18n.global.t('import') }} + {{ i18n.global.t('export') }}
@@ -47,6 +61,7 @@ const localPermission = ref( props.permissions.map((e) => ({ name: e.name, permission: e.permission ?? 0, + permissionNumber: e.name === 'members' ? 5 : 3, })), ); diff --git a/src/vueLib/login/userStore.ts b/src/vueLib/login/userStore.ts index d865f67..d96196a 100644 --- a/src/vueLib/login/userStore.ts +++ b/src/vueLib/login/userStore.ts @@ -24,7 +24,7 @@ export const useUserStore = defineStore('user', { }; }, isPermittedTo: (state: UserState) => { - return (name: string, type: 'read' | 'write' | 'delete'): boolean => { + return (name: string, type: 'read' | 'write' | 'delete' | 'import' | 'export'): boolean => { const permission = state.user?.permissions?.find((r: Permission) => r.name === name); switch (type) { case 'read': @@ -33,6 +33,10 @@ export const useUserStore = defineStore('user', { return permission?.permission ? (permission.permission & (1 << 1)) === 2 : false; case 'delete': return permission?.permission ? (permission.permission & (1 << 2)) === 4 : false; + case 'import': + return permission?.permission ? (permission.permission & (1 << 3)) === 8 : false; + case 'export': + return permission?.permission ? (permission.permission & (1 << 4)) === 16 : false; } }; }, diff --git a/src/vueLib/tables/members/MembersTable.ts b/src/vueLib/tables/members/MembersTable.ts index 3af8ec6..0c5074f 100644 --- a/src/vueLib/tables/members/MembersTable.ts +++ b/src/vueLib/tables/members/MembersTable.ts @@ -4,6 +4,7 @@ import type { Member, Members } from 'src/vueLib/models/member'; import { useNotify } from 'src/vueLib/general/useNotify'; import { i18n } from 'boot/lang'; import { useResponsibleTable } from '../responsible/ResponsibleTable'; +import { appName } from 'src/vueLib/models/settings'; export function useMemberTable() { const members = ref([]); @@ -253,6 +254,47 @@ export function useMemberTable() { }); } + function exportCsv() { + const comma = ';'; + // Extract only columns that have a field (not icons/options) + const exportableColumns = columns.value.filter( + (col) => typeof col.field === 'string' && col.field !== 'cake' && col.field !== 'option', + ) as { field: keyof Member; label: string }[]; + + // Build CSV header row + const header = exportableColumns.map((col) => col.field).join(comma); + + // Build CSV rows + const data = members.value.map((member) => + exportableColumns + .map((col) => { + const value = member[col.field]; + // handle nested objects (e.g. responsiblePerson) + if (typeof value === 'object' && value !== null) { + if ('firstName' in value && 'lastName' in value) + return `"${value.firstName} ${value.lastName}"`; + return `"${JSON.stringify(value)}"`; + } + return `"${value ?? ''}"`; + }) + .join(comma), + ); + + // Combine into CSV string + const csv = [header, ...data].join('\n'); + + // Create blob and trigger download + const BOM = '\uFEFF'; + const blob = new Blob([BOM + csv], { type: 'text/csv;charset=utf-8;' }); + const url = URL.createObjectURL(blob); + const link = document.createElement('a'); + link.href = url; + link.setAttribute('download', i18n.global.t(appName.value) + '.csv'); + document.body.appendChild(link); + link.click(); + document.body.removeChild(link); + } + return { members, responsibles, @@ -263,5 +305,6 @@ export function useMemberTable() { updateMembers, isXDaysBeforeAnnualDate, disableColumns, + exportCsv, }; } diff --git a/src/vueLib/tables/members/MembersTable.vue b/src/vueLib/tables/members/MembersTable.vue index 8219b49..48733a2 100644 --- a/src/vueLib/tables/members/MembersTable.vue +++ b/src/vueLib/tables/members/MembersTable.vue @@ -43,7 +43,7 @@ {{ $t('selectMemberOptions') }} {{ $t('importCSV') }} + + {{ $t('exportCSV') }} +