added first come first serve waitinglist with email notification

This commit is contained in:
Rajpreet0 2026-08-27 18:56:35 +02:00
parent 7548473174
commit bd10088e26
9 changed files with 594 additions and 56 deletions

View file

@ -0,0 +1,30 @@
-- CreateTable
CREATE TABLE "CourseWaitlistEntry" (
"id" TEXT NOT NULL,
"courseId" TEXT NOT NULL,
"userId" TEXT,
"guestEmail" TEXT,
"guestName" TEXT,
"notifiedAt" TIMESTAMP(3),
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
CONSTRAINT "CourseWaitlistEntry_pkey" PRIMARY KEY ("id")
);
-- CreateIndex
CREATE INDEX "CourseWaitlistEntry_courseId_idx" ON "CourseWaitlistEntry"("courseId");
-- CreateIndex
CREATE INDEX "CourseWaitlistEntry_userId_idx" ON "CourseWaitlistEntry"("userId");
-- CreateIndex
CREATE UNIQUE INDEX "CourseWaitlistEntry_courseId_userId_key" ON "CourseWaitlistEntry"("courseId", "userId");
-- CreateIndex
CREATE UNIQUE INDEX "CourseWaitlistEntry_courseId_guestEmail_key" ON "CourseWaitlistEntry"("courseId", "guestEmail");
-- AddForeignKey
ALTER TABLE "CourseWaitlistEntry" ADD CONSTRAINT "CourseWaitlistEntry_courseId_fkey" FOREIGN KEY ("courseId") REFERENCES "Course"("id") ON DELETE CASCADE ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "CourseWaitlistEntry" ADD CONSTRAINT "CourseWaitlistEntry_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User"("id") ON DELETE CASCADE ON UPDATE CASCADE;

View file

@ -17,32 +17,33 @@ datasource db {
}
model User {
id String @id @default(uuid())
name String?
complaints String?
birthday DateTime?
plz String?
city String?
street String?
houseNumber String?
id String @id @default(uuid())
name String?
complaints String?
birthday DateTime?
plz String?
city String?
street String?
houseNumber String?
// @unique is Prisma-managed and stays case-sensitive at the schema level —
// Prisma has no functional-index syntax. The actual enforced constraint is
// a case-insensitive unique index on lower(email), added via raw SQL in
// migration 20260817000000_case_insensitive_user_email. Do not remove
// @unique here; it keeps Prisma's drift detection from trying to recreate
// a duplicate case-sensitive constraint.
email String @unique
tel String?
gender String?
is2FA Boolean @default(false)
isSendingMail Boolean @default(false)
isNewsletter Boolean @default(false)
pbSrc String?
isValid Boolean @default(false)
trainings UserTraining[]
courseBookings CourseBooking[]
createdAt DateTime? @default(now())
updatedAt DateTime? @updatedAt
email String @unique
tel String?
gender String?
is2FA Boolean @default(false)
isSendingMail Boolean @default(false)
isNewsletter Boolean @default(false)
pbSrc String?
isValid Boolean @default(false)
trainings UserTraining[]
courseBookings CourseBooking[]
courseWaitlistEntries CourseWaitlistEntry[]
createdAt DateTime? @default(now())
updatedAt DateTime? @updatedAt
}
model Employee {
@ -122,10 +123,10 @@ model Business {
}
model Course {
id String @id @default(uuid())
id String @id @default(uuid())
name String
sport String[]
level String @default("any")
level String @default("any")
date DateTime
timeFrom String // Anfangszeit
timeTo String // Endzeit
@ -133,24 +134,25 @@ model Course {
room String
description String // Rich text HTML content
maxParticipants Int // Maximum number of participants
price Float @default(10.0) // Preis in Euro
isStandingOrder Boolean @default(false)
price Float @default(10.0) // Preis in Euro
isStandingOrder Boolean @default(false)
frequency String? // daily, weekly, biweekly, monthly, custom
weekdays String[] // For custom frequency
weekdayTimings Json? // { "monday": { "timeFrom": "08:00", "timeTo": "09:00" }, ... }
endDate DateTime? // Required when isStandingOrder = true
parentCourseId String? // Null on template; set on every child instance
parentCourse Course? @relation("StandingOrderInstances", fields: [parentCourseId], references: [id], onDelete: Cascade)
children Course[] @relation("StandingOrderInstances")
status String @default("draft") // draft, published, cancelled
parentCourse Course? @relation("StandingOrderInstances", fields: [parentCourseId], references: [id], onDelete: Cascade)
children Course[] @relation("StandingOrderInstances")
status String @default("draft") // draft, published, cancelled
cancelledAt DateTime? // Zeitpunkt der Kursabsage (nur gesetzt wenn status = "cancelled")
createdBy String // Manager ID who created the course
businessId String?
coverImage String?
business Business? @relation(fields: [businessId], references: [id], onDelete: SetNull)
business Business? @relation(fields: [businessId], references: [id], onDelete: SetNull)
bookings CourseBooking[]
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
waitlistEntries CourseWaitlistEntry[]
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
@@index([createdBy])
@@index([date])
@ -204,3 +206,20 @@ model CourseBooking {
@@index([userId])
@@index([guestLeadId])
}
model CourseWaitlistEntry {
id String @id @default(uuid())
courseId String
userId String?
guestEmail String? // normalisiert lowercase; null wenn userId gesetzt
guestName String? // optional, nur bei Gast-Einträgen
notifiedAt DateTime? // Zeitpunkt der letzten "Platz frei"-Benachrichtigung, null = noch nie benachrichtigt
course Course @relation(fields: [courseId], references: [id], onDelete: Cascade)
user User? @relation(fields: [userId], references: [id], onDelete: Cascade)
createdAt DateTime @default(now())
@@unique([courseId, userId]) // Ein User kann sich nur einmal pro Kurs eintragen (NULL zählt in Postgres nicht als Duplikat, betrifft also nur registrierte User)
@@unique([courseId, guestEmail]) // Dieselbe Gast-E-Mail nur einmal pro Kurs (analog, betrifft nur Gast-Einträge)
@@index([courseId])
@@index([userId])
}