Ship loyalty club and form builder so production pull gets the new screens.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-09-06 13:50:05 +03:30
co-authored by Cursor
parent 175f1fa440
commit 820c0780d8
21 changed files with 3658 additions and 893 deletions
@@ -0,0 +1,159 @@
-- CreateTable
CREATE TABLE IF NOT EXISTS "LoyaltySettings" (
"id" TEXT NOT NULL,
"organizationId" TEXT NOT NULL,
"enabled" BOOLEAN NOT NULL DEFAULT true,
"welcomePoints" INTEGER NOT NULL DEFAULT 0,
"notifyOnAdjust" BOOLEAN NOT NULL DEFAULT false,
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" TIMESTAMP(3) NOT NULL,
CONSTRAINT "LoyaltySettings_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE IF NOT EXISTS "LoyaltyLedger" (
"id" TEXT NOT NULL,
"organizationId" TEXT NOT NULL,
"personId" TEXT NOT NULL,
"delta" INTEGER NOT NULL,
"reason" TEXT,
"source" TEXT NOT NULL DEFAULT 'manual',
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
CONSTRAINT "LoyaltyLedger_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE IF NOT EXISTS "LoyaltyReward" (
"id" TEXT NOT NULL,
"organizationId" TEXT NOT NULL,
"name" TEXT NOT NULL,
"description" TEXT,
"pointsCost" INTEGER NOT NULL,
"isActive" BOOLEAN NOT NULL DEFAULT true,
"position" INTEGER NOT NULL DEFAULT 0,
"color" TEXT,
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" TIMESTAMP(3) NOT NULL,
CONSTRAINT "LoyaltyReward_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE IF NOT EXISTS "LoyaltyRedemption" (
"id" TEXT NOT NULL,
"organizationId" TEXT NOT NULL,
"personId" TEXT NOT NULL,
"rewardId" TEXT NOT NULL,
"pointsSpent" INTEGER NOT NULL,
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
CONSTRAINT "LoyaltyRedemption_pkey" PRIMARY KEY ("id")
);
-- CreateIndex
CREATE UNIQUE INDEX IF NOT EXISTS "LoyaltySettings_organizationId_key" ON "LoyaltySettings"("organizationId");
-- CreateIndex
CREATE INDEX IF NOT EXISTS "LoyaltyLedger_organizationId_createdAt_idx" ON "LoyaltyLedger"("organizationId", "createdAt");
-- CreateIndex
CREATE INDEX IF NOT EXISTS "LoyaltyLedger_personId_createdAt_idx" ON "LoyaltyLedger"("personId", "createdAt");
-- CreateIndex
CREATE INDEX IF NOT EXISTS "LoyaltyReward_organizationId_position_idx" ON "LoyaltyReward"("organizationId", "position");
-- CreateIndex
CREATE INDEX IF NOT EXISTS "LoyaltyRedemption_organizationId_createdAt_idx" ON "LoyaltyRedemption"("organizationId", "createdAt");
-- CreateIndex
CREATE INDEX IF NOT EXISTS "LoyaltyRedemption_personId_idx" ON "LoyaltyRedemption"("personId");
-- CreateIndex
CREATE INDEX IF NOT EXISTS "LoyaltyRedemption_rewardId_idx" ON "LoyaltyRedemption"("rewardId");
-- AddForeignKey
DO $$
BEGIN
IF NOT EXISTS (
SELECT 1 FROM pg_constraint WHERE conname = 'LoyaltySettings_organizationId_fkey'
) THEN
ALTER TABLE "LoyaltySettings"
ADD CONSTRAINT "LoyaltySettings_organizationId_fkey"
FOREIGN KEY ("organizationId") REFERENCES "Organization"("id")
ON DELETE CASCADE ON UPDATE CASCADE;
END IF;
END $$;
DO $$
BEGIN
IF NOT EXISTS (
SELECT 1 FROM pg_constraint WHERE conname = 'LoyaltyLedger_organizationId_fkey'
) THEN
ALTER TABLE "LoyaltyLedger"
ADD CONSTRAINT "LoyaltyLedger_organizationId_fkey"
FOREIGN KEY ("organizationId") REFERENCES "Organization"("id")
ON DELETE CASCADE ON UPDATE CASCADE;
END IF;
END $$;
DO $$
BEGIN
IF NOT EXISTS (
SELECT 1 FROM pg_constraint WHERE conname = 'LoyaltyLedger_personId_fkey'
) THEN
ALTER TABLE "LoyaltyLedger"
ADD CONSTRAINT "LoyaltyLedger_personId_fkey"
FOREIGN KEY ("personId") REFERENCES "Person"("id")
ON DELETE CASCADE ON UPDATE CASCADE;
END IF;
END $$;
DO $$
BEGIN
IF NOT EXISTS (
SELECT 1 FROM pg_constraint WHERE conname = 'LoyaltyReward_organizationId_fkey'
) THEN
ALTER TABLE "LoyaltyReward"
ADD CONSTRAINT "LoyaltyReward_organizationId_fkey"
FOREIGN KEY ("organizationId") REFERENCES "Organization"("id")
ON DELETE CASCADE ON UPDATE CASCADE;
END IF;
END $$;
DO $$
BEGIN
IF NOT EXISTS (
SELECT 1 FROM pg_constraint WHERE conname = 'LoyaltyRedemption_organizationId_fkey'
) THEN
ALTER TABLE "LoyaltyRedemption"
ADD CONSTRAINT "LoyaltyRedemption_organizationId_fkey"
FOREIGN KEY ("organizationId") REFERENCES "Organization"("id")
ON DELETE CASCADE ON UPDATE CASCADE;
END IF;
END $$;
DO $$
BEGIN
IF NOT EXISTS (
SELECT 1 FROM pg_constraint WHERE conname = 'LoyaltyRedemption_personId_fkey'
) THEN
ALTER TABLE "LoyaltyRedemption"
ADD CONSTRAINT "LoyaltyRedemption_personId_fkey"
FOREIGN KEY ("personId") REFERENCES "Person"("id")
ON DELETE CASCADE ON UPDATE CASCADE;
END IF;
END $$;
DO $$
BEGIN
IF NOT EXISTS (
SELECT 1 FROM pg_constraint WHERE conname = 'LoyaltyRedemption_rewardId_fkey'
) THEN
ALTER TABLE "LoyaltyRedemption"
ADD CONSTRAINT "LoyaltyRedemption_rewardId_fkey"
FOREIGN KEY ("rewardId") REFERENCES "LoyaltyReward"("id")
ON DELETE RESTRICT ON UPDATE CASCADE;
END IF;
END $$;
@@ -0,0 +1,31 @@
ALTER TABLE "LeadForm" ADD COLUMN IF NOT EXISTS "description" TEXT;
ALTER TABLE "LeadForm" ADD COLUMN IF NOT EXISTS "submitLabel" TEXT NOT NULL DEFAULT 'ارسال درخواست';
ALTER TABLE "LeadForm" ADD COLUMN IF NOT EXISTS "submitColor" TEXT NOT NULL DEFAULT '#14b8a6';
ALTER TABLE "LeadForm" ADD COLUMN IF NOT EXISTS "coverStorageKey" TEXT;
ALTER TABLE "LeadFormField" ADD COLUMN IF NOT EXISTS "placeholder" TEXT;
ALTER TABLE "LeadFormField" ADD COLUMN IF NOT EXISTS "options" JSONB;
CREATE TABLE IF NOT EXISTS "LeadFormSubmission" (
"id" TEXT NOT NULL,
"formId" TEXT NOT NULL,
"values" JSONB NOT NULL,
"personId" TEXT,
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
CONSTRAINT "LeadFormSubmission_pkey" PRIMARY KEY ("id")
);
CREATE INDEX IF NOT EXISTS "LeadFormSubmission_formId_createdAt_idx" ON "LeadFormSubmission"("formId", "createdAt");
DO $$
BEGIN
IF NOT EXISTS (
SELECT 1 FROM pg_constraint WHERE conname = 'LeadFormSubmission_formId_fkey'
) THEN
ALTER TABLE "LeadFormSubmission"
ADD CONSTRAINT "LeadFormSubmission_formId_fkey"
FOREIGN KEY ("formId") REFERENCES "LeadForm"("id")
ON DELETE CASCADE ON UPDATE CASCADE;
END IF;
END $$;
+93 -10
View File
@@ -161,7 +161,11 @@ model Organization {
portalOtps PortalOtp[]
projectBoards ProjectBoard[]
projectCards ProjectCard[]
loyaltyTiers LoyaltyTier[]
loyaltyTiers LoyaltyTier[]
loyaltySettings LoyaltySettings?
loyaltyLedgers LoyaltyLedger[]
loyaltyRewards LoyaltyReward[]
loyaltyRedemptions LoyaltyRedemption[]
automationRules AutomationRule[]
memoryItems MemoryItem[]
noteAiSuggestions NoteAiSuggestion[]
@@ -256,6 +260,8 @@ model Person {
loyaltyPoints Int @default(0)
loyaltyTierId String?
loyaltyTier LoyaltyTier? @relation(fields: [loyaltyTierId], references: [id], onDelete: SetNull)
loyaltyLedgers LoyaltyLedger[]
loyaltyRedemptions LoyaltyRedemption[]
deals Deal[]
activities Activity[]
smartNotes Note[]
@@ -581,6 +587,7 @@ model LeadForm {
organizationId String
organization Organization @relation(fields: [organizationId], references: [id], onDelete: Cascade)
name String
description String?
publicToken String @unique @default(cuid())
isActive Boolean @default(true)
createPerson Boolean @default(true)
@@ -591,7 +598,11 @@ model LeadForm {
redirectUrl String?
assigneeIds String[] @default([])
roundRobinIndex Int @default(0)
submitLabel String @default("ارسال درخواست")
submitColor String @default("#14b8a6")
coverStorageKey String?
fields LeadFormField[]
submissions LeadFormSubmission[]
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
@@ -599,20 +610,33 @@ model LeadForm {
}
model LeadFormField {
id String @id @default(cuid())
formId String
form LeadForm @relation(fields: [formId], references: [id], onDelete: Cascade)
key String
label String
fieldType String @default("text")
required Boolean @default(false)
mapTo String
position Int @default(0)
id String @id @default(cuid())
formId String
form LeadForm @relation(fields: [formId], references: [id], onDelete: Cascade)
key String
label String
fieldType String @default("text")
required Boolean @default(false)
mapTo String
position Int @default(0)
placeholder String?
options Json?
@@unique([formId, key])
@@index([formId, position])
}
model LeadFormSubmission {
id String @id @default(cuid())
formId String
form LeadForm @relation(fields: [formId], references: [id], onDelete: Cascade)
values Json
personId String?
createdAt DateTime @default(now())
@@index([formId, createdAt])
}
model Quote {
id String @id @default(cuid())
organizationId String
@@ -958,6 +982,65 @@ model LoyaltyTier {
@@index([organizationId, minPoints])
}
model LoyaltySettings {
id String @id @default(cuid())
organizationId String @unique
organization Organization @relation(fields: [organizationId], references: [id], onDelete: Cascade)
enabled Boolean @default(true)
welcomePoints Int @default(0)
notifyOnAdjust Boolean @default(false)
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
}
model LoyaltyLedger {
id String @id @default(cuid())
organizationId String
organization Organization @relation(fields: [organizationId], references: [id], onDelete: Cascade)
personId String
person Person @relation(fields: [personId], references: [id], onDelete: Cascade)
delta Int
reason String?
source String @default("manual")
createdAt DateTime @default(now())
@@index([organizationId, createdAt])
@@index([personId, createdAt])
}
model LoyaltyReward {
id String @id @default(cuid())
organizationId String
organization Organization @relation(fields: [organizationId], references: [id], onDelete: Cascade)
name String
description String?
pointsCost Int
isActive Boolean @default(true)
position Int @default(0)
color String?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
redemptions LoyaltyRedemption[]
@@index([organizationId, position])
}
model LoyaltyRedemption {
id String @id @default(cuid())
organizationId String
organization Organization @relation(fields: [organizationId], references: [id], onDelete: Cascade)
personId String
person Person @relation(fields: [personId], references: [id], onDelete: Cascade)
rewardId String
reward LoyaltyReward @relation(fields: [rewardId], references: [id], onDelete: Restrict)
pointsSpent Int
createdAt DateTime @default(now())
@@index([organizationId, createdAt])
@@index([personId])
@@index([rewardId])
}
model AutomationRule {
id String @id @default(cuid())
organizationId String