diff --git a/app/package-lock.json b/app/package-lock.json index 767fea8..dad8bd4 100644 --- a/app/package-lock.json +++ b/app/package-lock.json @@ -9,6 +9,7 @@ "version": "0.1.0", "dependencies": { "ical-generator": "^10.2.0", + "papaparse": "^5.5.3", "pocketbase": "^0.26.9", "rrule": "^2.8.1", "web-push": "^3.6.7" @@ -17,6 +18,7 @@ "@sveltejs/adapter-node": "^5.5.4", "@sveltejs/kit": "^2.57.0", "@sveltejs/vite-plugin-svelte": "^7.0.0", + "@types/papaparse": "^5.5.2", "@types/web-push": "^3.6.4", "svelte": "^5.55.2", "svelte-check": "^4.4.6", @@ -2655,6 +2657,16 @@ "undici-types": ">=7.24.0 <7.24.7" } }, + "node_modules/@types/papaparse": { + "version": "5.5.2", + "resolved": "https://registry.npmjs.org/@types/papaparse/-/papaparse-5.5.2.tgz", + "integrity": "sha512-gFnFp/JMzLHCwRf7tQHrNnfhN4eYBVYYI897CGX4MY1tzY9l2aLkVyx2IlKZ/SAqDbB3I1AOZW5gTMGGsqWliA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, "node_modules/@types/resolve": { "version": "1.20.2", "resolved": "https://registry.npmjs.org/@types/resolve/-/resolve-1.20.2.tgz", @@ -5090,6 +5102,12 @@ "dev": true, "license": "BlueOak-1.0.0" }, + "node_modules/papaparse": { + "version": "5.5.3", + "resolved": "https://registry.npmjs.org/papaparse/-/papaparse-5.5.3.tgz", + "integrity": "sha512-5QvjGxYVjxO59MGU2lHVYpRWBBtKHnlIAcSe1uNFCkkptUh63NFRj0FJQm7nR67puEruUci/ZkjmEFrjCAyP4A==", + "license": "MIT" + }, "node_modules/path-key": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", diff --git a/app/package.json b/app/package.json index fd9187a..3baa302 100644 --- a/app/package.json +++ b/app/package.json @@ -15,6 +15,7 @@ "@sveltejs/adapter-node": "^5.5.4", "@sveltejs/kit": "^2.57.0", "@sveltejs/vite-plugin-svelte": "^7.0.0", + "@types/papaparse": "^5.5.2", "@types/web-push": "^3.6.4", "svelte": "^5.55.2", "svelte-check": "^4.4.6", @@ -26,6 +27,7 @@ }, "dependencies": { "ical-generator": "^10.2.0", + "papaparse": "^5.5.3", "pocketbase": "^0.26.9", "rrule": "^2.8.1", "web-push": "^3.6.7" diff --git a/app/src/routes/(app)/einstellungen/+page.svelte b/app/src/routes/(app)/einstellungen/+page.svelte index acc793b..fb98046 100644 --- a/app/src/routes/(app)/einstellungen/+page.svelte +++ b/app/src/routes/(app)/einstellungen/+page.svelte @@ -330,6 +330,10 @@
+ Import / Export + +
+ {/if} @@ -482,6 +486,14 @@ cursor: pointer; align-self: flex-start; } + .btn-importexport { + display: block; width: 100%; padding: 0.75rem; + background: none; border: 1.5px solid #e2e8f0; border-radius: 8px; + font-size: 0.95rem; color: #1e40af; text-align: center; + text-decoration: none; margin-bottom: 0; + } + .btn-importexport:hover { border-color: #1e40af; background: #f0f9ff; } + .btn-logout { width: 100%; padding: 0.75rem; diff --git a/app/src/routes/(app)/import-export/+page.svelte b/app/src/routes/(app)/import-export/+page.svelte new file mode 100644 index 0000000..dd8c60c --- /dev/null +++ b/app/src/routes/(app)/import-export/+page.svelte @@ -0,0 +1,432 @@ + + +Import / Export — vereins.haus + +
+ ← Einstellungen +

Import / Export

+
+ +{#if loading} +

Laden…

+{:else} + + +
+

Mitglieder exportieren

+
+ + + +
+ {#if exportStatus} +

✓ {exportStatus}

+ {/if} +
+ +
+

Datensicherung

+

Vollständige Sicherungskopie aller Vereinsdaten als JSON – für Archivierung oder Wechsel der Software (DSGVO Art. 20).

+ +
+ + +
+

Mitglieder importieren

+

CSV-Datei hochladen – die Spalten werden automatisch erkannt und können vor dem Import angepasst werden.

+ + {#if importPhase === 'idle'} + + + {:else if importPhase === 'mapping'} +
+ {csvRows.length} Zeilen erkannt · Spalten zuordnen: + +
+ +
+
+ CSV-Spalte + → Vereinshaus-Feld + Vorschau (1. Zeile) +
+ {#each csvHeaders as h} +
+ {h} + + {csvRows[0]?.[h] ?? ''} +
+ {/each} +
+ +
+

Vor- und Nachname sind Pflichtfelder.

+ +
+ + {:else if importPhase === 'done'} +
0}> +

✓ {importResult.ok} Mitglieder importiert

+ {#if importResult.fehler.length > 0} +

{importResult.fehler.length} Probleme:

+ + {/if} + +
+ {/if} +
+ +{/if} + +