diff --git a/app/src/lib/types.ts b/app/src/lib/types.ts index 17071d1..ef52edf 100644 --- a/app/src/lib/types.ts +++ b/app/src/lib/types.ts @@ -42,11 +42,22 @@ export interface Mitglied { mandatsdatum?: string; } +export type Rolle = 'admin' | 'trainer'; + export interface Gruppe { id: string; verein_id: string; name: string; beschreibung?: string; + trainer_ids: string[]; +} + +export interface Einladung { + id: string; + verein_id: string; + rolle: Rolle; + token: string; + genutzt: boolean; } export interface Beitrag { diff --git a/app/src/routes/(app)/+layout.svelte b/app/src/routes/(app)/+layout.svelte index f18d99c..f411994 100644 --- a/app/src/routes/(app)/+layout.svelte +++ b/app/src/routes/(app)/+layout.svelte @@ -67,14 +67,18 @@ return Uint8Array.from([...raw].map((c) => c.charCodeAt(0))); } - const navItems: { href: string; label: string; icon: IconName }[] = [ - { href: '/', label: 'Übersicht', icon: 'house' }, - { href: '/mitglieder', label: 'Mitglieder', icon: 'users' }, - { href: '/termine', label: 'Termine', icon: 'calendar' }, - { href: '/beitraege', label: 'Beiträge', icon: 'currency-eur' }, - { href: '/nachrichten', label: 'Nachrichten', icon: 'envelope' }, - { href: '/einstellungen', label: 'Einstellungen', icon: 'gear' }, + const isAdmin = () => !pb.authStore.record?.rolle || pb.authStore.record?.rolle === 'admin'; + + const allNavItems: { href: string; label: string; icon: IconName; adminOnly?: boolean }[] = [ + { href: '/', label: 'Übersicht', icon: 'house' }, + { href: '/mitglieder', label: 'Mitglieder', icon: 'users' }, + { href: '/termine', label: 'Termine', icon: 'calendar' }, + { href: '/beitraege', label: 'Beiträge', icon: 'currency-eur', adminOnly: true }, + { href: '/nachrichten', label: 'Nachrichten', icon: 'envelope' }, + { href: '/einstellungen', label: 'Einstellungen', icon: 'gear' }, ]; + + const navItems = $derived(allNavItems.filter(i => !i.adminOnly || isAdmin()));
diff --git a/app/src/routes/(app)/beitraege/+page.svelte b/app/src/routes/(app)/beitraege/+page.svelte index 0383cba..bc820d8 100644 --- a/app/src/routes/(app)/beitraege/+page.svelte +++ b/app/src/routes/(app)/beitraege/+page.svelte @@ -1,5 +1,6 @@ + +Einladung — vereins.haus + +
+ + +
+ {#if loading} +

Prüfe Einladung…

+ + {:else if fehler} +
+

Ungültige Einladung

+

{fehler}

+ + {:else} +
🎉
+

Du wurdest eingeladen!

+

+ Du wirst als {einladung?.rolle === 'trainer' ? 'Trainer' : 'Admin'} + zum Verein „{vereinName}" hinzugefügt. +

+ +
{ e.preventDefault(); registrieren(); }}> +
+ + +
+
+ + +
+
+ + +
+
+ + +
+ + {#if formError} +

{formError}

+ {/if} + + +
+ {/if} +
+
+ + diff --git a/pocketbase/pb_migrations/1779230500_rollen.js b/pocketbase/pb_migrations/1779230500_rollen.js new file mode 100644 index 0000000..a998ab1 --- /dev/null +++ b/pocketbase/pb_migrations/1779230500_rollen.js @@ -0,0 +1,92 @@ +/// +migrate((app) => { + + // Users: +rolle, listRule für verein-weite Sichtbarkeit + { + const c = app.findCollectionByNameOrId("_pb_users_auth_") + c.fields.addAt(99, new Field({ + "type": "select", "id": "select2001000070", "name": "rolle", + "help": "", "hidden": false, "presentable": false, "required": false, "system": false, + "maxSelect": 1, "values": ["admin", "trainer"] + })) + c.listRule = "@request.auth.verein_id = verein_id" + app.save(c) + } + + // Gruppen: +trainer_ids (welche User betreuen diese Gruppe) + { + const c = app.findCollectionByNameOrId("pbc_3099069179") + c.fields.addAt(99, new Field({ + "type": "relation", "id": "relation2001000071", "name": "trainer_ids", + "help": "", "hidden": false, "presentable": false, "required": false, "system": false, + "cascadeDelete": false, "collectionId": "_pb_users_auth_", + "maxSelect": 99, "minSelect": 0 + })) + app.save(c) + } + + // Einladungen-Collection + { + const c = new Collection({ + "createRule": "@request.auth.verein_id = verein_id", + "deleteRule": "@request.auth.verein_id = verein_id", + "listRule": "@request.auth.verein_id = verein_id", + "viewRule": "", + "updateRule": "@request.auth.verein_id = verein_id", + "fields": [ + { + "autogeneratePattern": "[a-z0-9]{15}", "id": "text3208210256", + "max": 15, "min": 15, "name": "id", "pattern": "^[a-z0-9]+$", + "primaryKey": true, "required": true, "system": true, "type": "text", + "help": "", "hidden": false, "presentable": false + }, + { + "type": "relation", "id": "relation2001000072", "name": "verein_id", + "help": "", "hidden": false, "presentable": false, "required": true, "system": false, + "cascadeDelete": true, "collectionId": "pbc_3589557411", "maxSelect": 1, "minSelect": 0 + }, + { + "type": "select", "id": "select2001000073", "name": "rolle", + "help": "", "hidden": false, "presentable": false, "required": true, "system": false, + "maxSelect": 1, "values": ["admin", "trainer"] + }, + { + "type": "text", "id": "text2001000074", "name": "token", + "help": "", "hidden": false, "presentable": false, "required": true, "system": false, + "autogeneratePattern": "", "min": 0, "max": 0, "pattern": "" + }, + { + "type": "bool", "id": "bool2001000075", "name": "genutzt", + "help": "", "hidden": false, "presentable": false, "required": false, "system": false + } + ], + "id": "pbc_einladungen", + "indexes": ["CREATE UNIQUE INDEX idx_einladungen_token ON einladungen (token)"], + "name": "einladungen", + "system": false, + "type": "base" + }) + app.save(c) + } + +}, (app) => { + + { + const c = app.findCollectionByNameOrId("_pb_users_auth_") + c.fields.removeById("select2001000070") + c.listRule = "" + app.save(c) + } + + { + const c = app.findCollectionByNameOrId("pbc_3099069179") + c.fields.removeById("relation2001000071") + app.save(c) + } + + { + const c = app.findCollectionByNameOrId("pbc_einladungen") + app.delete(c) + } + +})