Kein fertiger Stand, sondern ein Zwischenstand: Hier wurde im September 2026 angefangen, die App auf „Kurbeler" umzubenennen — Wortmarke, Icons, brand.ts, Benachrichtigungen, das Benutzerverzeichnis. Weitergegangen ist es dann im eigenen Repo `~/Dokumente/Projekte/kurbeler`, das die vollständige Historie dieses Repos mitgenommen hat. Committet wird das hier nicht, weil es gebraucht würde, sondern damit es nicht als Haufen unversionierter Dateien im Arbeitsverzeichnis verrottet. Wer in zwei Jahren nachsieht, findet so eine Geschichte statt eines Rätsels — und das Stammtisch-Wappen, das nur hier je existiert hat. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LTw6xVYgjMy9AQtfs1Gdyu
83 lines
3.2 KiB
JavaScript
83 lines
3.2 KiB
JavaScript
/// <reference path="../pb_data/types.d.ts" />
|
|
|
|
// Persönliche Einladungen — der zweite Weg zu einem Konto.
|
|
//
|
|
// Bis hierher gab es zwei Wege ins Team, und einer davon war schief:
|
|
//
|
|
// 1. Der Einladungslink. Er gilt für jeden, der ihn hat; wer ihn einlöst,
|
|
// legt sich selbst ein Konto an und vergibt sein eigenes Passwort.
|
|
// Dieser Weg bleibt unverändert.
|
|
//
|
|
// 2. „Konto anlegen" im Kader. Dort tippte die Teamleitung eine Adresse
|
|
// und ein Passwort ein — und musste dieses Passwort anschließend
|
|
// irgendwie zum Fahrer bringen. Über einen Kanal, den niemand geplant
|
|
// hat, im Zweifel per WhatsApp. Sie kannte es außerdem, und der Fahrer
|
|
// hatte ein Passwort, das er sich nicht ausgesucht hat.
|
|
//
|
|
// Der zweite Weg wird zur Einladungsmail: Die Teamleitung gibt nur noch die
|
|
// Adresse an, PocketBase verschickt einen Link, und der Fahrer setzt sein
|
|
// Passwort selbst. Niemand außer ihm kennt es je.
|
|
//
|
|
// Dafür bekommt `team_invites` drei Felder. Eine persönliche Einladung ist
|
|
// derselbe Datensatz wie ein Link, nur mit Adressat:
|
|
//
|
|
// email — an wen sie ging. Gesetzt heißt: Diese Einladung gilt nur für
|
|
// diese Adresse, die Anmeldeseite lässt keine andere zu.
|
|
// rider — welcher Fahrer sein Konto bekommt. Ohne dieses Feld legte der
|
|
// Beitritt einen zweiten Fahrer an und dieselbe Person stünde
|
|
// doppelt im Kader. cascadeDelete: Verschwindet der Fahrer,
|
|
// verfällt seine Einladung mit ihm.
|
|
// sent — wann die Mail rausging. Nur zur Anzeige („vor 3 Tagen
|
|
// eingeladen"), damit niemand raten muss, ob schon eine unterwegs
|
|
// ist.
|
|
//
|
|
// Alle drei sind optional. Ein Einladungslink lässt sie leer und verhält
|
|
// sich wie zuvor — die Felder erweitern den Datensatz, sie verändern ihn
|
|
// nicht.
|
|
|
|
migrate((app) => {
|
|
const invites = app.findCollectionByNameOrId('team_invites')
|
|
const riders = app.findCollectionByNameOrId('riders')
|
|
|
|
if (!invites.fields.getByName('email')) {
|
|
invites.fields.add(new Field({
|
|
id: 'email_invite_email',
|
|
name: 'email',
|
|
type: 'text',
|
|
}))
|
|
}
|
|
|
|
if (!invites.fields.getByName('rider')) {
|
|
invites.fields.add(new Field({
|
|
id: 'relation_invite_rider',
|
|
name: 'rider',
|
|
type: 'relation',
|
|
required: false,
|
|
maxSelect: 1,
|
|
collectionId: riders.id,
|
|
cascadeDelete: true,
|
|
}))
|
|
}
|
|
|
|
if (!invites.fields.getByName('sent')) {
|
|
invites.fields.add(new Field({
|
|
id: 'date_invite_sent',
|
|
name: 'sent',
|
|
type: 'date',
|
|
}))
|
|
}
|
|
|
|
// Die createRule bleibt, wie sie ist: Einladungen legt die Teamleitung
|
|
// an. Persönliche entstehen ohnehin nicht über die Collection, sondern
|
|
// über POST /api/team-invites/send — der Hook prüft dieselbe Bedingung
|
|
// und verschickt in einem Zug die Mail.
|
|
app.save(invites)
|
|
}, (app) => {
|
|
const invites = app.findCollectionByNameOrId('team_invites')
|
|
|
|
invites.fields.removeByName('email')
|
|
invites.fields.removeByName('rider')
|
|
invites.fields.removeByName('sent')
|
|
|
|
app.save(invites)
|
|
})
|