mirror of
https://github.com/Since-3/Kuma.git
synced 2026-10-12 20:51:18 +00:00
added first come first serve waitinglist with email notification
This commit is contained in:
parent
7548473174
commit
bd10088e26
9 changed files with 594 additions and 56 deletions
|
|
@ -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;
|
||||
|
|
@ -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])
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue