feat(backend): Wahl-Phasen, Verantwortliche-Invites, Auth fixes
- New VerantwortlicheInvite model: LT-issued invites so a person can register as Gemeinde Verantwortliche(r) for a specific Gemeinde, skipping the self-registration approval step. - Wahl/Workshop/Teilnehmer gain phase support (phasenAnzahl, beschreibung), mirroring the WP plugin's multi-phase elections. Teilnehmer unique constraint now scoped per phase. - Auth: team login + guest auth adjustments, spec coverage. - sync.service.ts: register VerantwortlicheInvite as a synced model. - wahl.service.ts: submitTeilnehmer updated for the new phase-scoped unique key. - client: login/home screen rework, new theme.dart, FCM web tweaks. - .gitignore: ignore .DS_Store. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
+40
-2
@@ -24,6 +24,7 @@ model Kc {
|
||||
guests GuestAccount[]
|
||||
localUsers User[]
|
||||
teamerInvites TeamerInvite[]
|
||||
verantwortlicheInvites VerantwortlicheInvite[]
|
||||
}
|
||||
|
||||
/// A local congregation/community participating in one Kc.
|
||||
@@ -37,6 +38,7 @@ model Gemeinde {
|
||||
memberships Membership[]
|
||||
guests GuestAccount[]
|
||||
teamerInvites TeamerInvite[]
|
||||
verantwortlicheInvites VerantwortlicheInvite[]
|
||||
|
||||
@@unique([kcId, name])
|
||||
}
|
||||
@@ -152,13 +154,42 @@ model TeamerInvite {
|
||||
gemeinde Gemeinde @relation(fields: [gemeindeId], references: [id], onDelete: Cascade)
|
||||
}
|
||||
|
||||
/// Invitation issued by a Leitungsteam member so a person can register as
|
||||
/// Gemeinde Verantwortliche/r for a specific Gemeinde via their
|
||||
/// Konfi-Castle-ID (Authentik) — skips the self-registration approval step
|
||||
/// since a Leitungsteam member is vouching for them directly. A group link
|
||||
/// leaves `email` null and may be redeemed up to `maxUses` times (null =
|
||||
/// unlimited); a personal invite pins `email` and defaults to a single use.
|
||||
model VerantwortlicheInvite {
|
||||
id String @id @default(cuid())
|
||||
kcId String
|
||||
gemeindeId String
|
||||
token String @unique
|
||||
email String?
|
||||
maxUses Int?
|
||||
usedCount Int @default(0)
|
||||
expiresAt DateTime?
|
||||
revokedAt DateTime?
|
||||
createdByUserId String
|
||||
createdAt DateTime @default(now())
|
||||
|
||||
kc Kc @relation(fields: [kcId], references: [id], onDelete: Cascade)
|
||||
gemeinde Gemeinde @relation(fields: [gemeindeId], references: [id], onDelete: Cascade)
|
||||
}
|
||||
|
||||
/// A workshop election, scoped to a Kc; name carries a date key + "Teil".
|
||||
/// `phasenAnzahl` mirrors the WP plugin's `anzahl_einheiten`: a Wahl can run
|
||||
/// several independent phases (e.g. morning/afternoon), each with its own
|
||||
/// workshops, its own guest submission, and its own assignment run — a guest
|
||||
/// submits once per phase, not once for the whole Wahl.
|
||||
model Wahl {
|
||||
id String @id @default(cuid())
|
||||
kcId String
|
||||
name String
|
||||
datumsSchluessel String
|
||||
teil String
|
||||
beschreibung String?
|
||||
phasenAnzahl Int @default(1)
|
||||
isOpen Boolean @default(true)
|
||||
createdAt DateTime @default(now())
|
||||
|
||||
@@ -168,10 +199,14 @@ model Wahl {
|
||||
forceZuteilungen ForceZuteilung[]
|
||||
}
|
||||
|
||||
/// A workshop offered in one phase of a Wahl. `phase` is 1-based and must be
|
||||
/// <= the owning Wahl's `phasenAnzahl`.
|
||||
model Workshop {
|
||||
id String @id @default(cuid())
|
||||
wahlId String
|
||||
phase Int @default(1)
|
||||
name String
|
||||
beschreibung String?
|
||||
kapazitaet Int
|
||||
minTeilnehmer Int @default(0)
|
||||
|
||||
@@ -180,10 +215,13 @@ model Workshop {
|
||||
forceZuteilungen ForceZuteilung[]
|
||||
}
|
||||
|
||||
/// A participant's submitted choices for a Wahl.
|
||||
/// A participant's submitted choices for one phase of a Wahl. A guest submits
|
||||
/// separately per phase (matching the WP plugin), so the same guest can have
|
||||
/// one row per (wahlId, phase).
|
||||
model Teilnehmer {
|
||||
id String @id @default(cuid())
|
||||
wahlId String
|
||||
phase Int @default(1)
|
||||
guestAccountId String
|
||||
prioritaeten Json
|
||||
createdAt DateTime @default(now())
|
||||
@@ -193,7 +231,7 @@ model Teilnehmer {
|
||||
zuteilung Zuteilung?
|
||||
forceZuteilung ForceZuteilung?
|
||||
|
||||
@@unique([wahlId, guestAccountId])
|
||||
@@unique([wahlId, guestAccountId, phase])
|
||||
}
|
||||
|
||||
/// Manual override set by LT before running the assignment algorithm; takes precedence.
|
||||
|
||||
Reference in New Issue
Block a user