feat: add DISC profiles and a sales copilot as an org module

Help reps capture a customer's likely behavior style and get negotiation guidance, with the feature gated from super admin.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-10-18 21:04:26 +03:30
co-authored by Cursor
parent 96befd3867
commit 002f697dfe
27 changed files with 2838 additions and 26 deletions
@@ -0,0 +1,97 @@
-- AlterTable
ALTER TABLE "Person" ADD COLUMN "discLabel" TEXT,
ADD COLUMN "discReliable" BOOLEAN NOT NULL DEFAULT false,
ADD COLUMN "discAssessedAt" TIMESTAMP(3);
-- CreateIndex
CREATE INDEX "Person_organizationId_discLabel_idx" ON "Person"("organizationId", "discLabel");
-- CreateTable
CREATE TABLE "DiscAssessment" (
"id" TEXT NOT NULL,
"organizationId" TEXT NOT NULL,
"personId" TEXT NOT NULL,
"assessorId" TEXT NOT NULL,
"answers" JSONB NOT NULL,
"scoreD" INTEGER NOT NULL,
"scoreI" INTEGER NOT NULL,
"scoreS" INTEGER NOT NULL,
"scoreC" INTEGER NOT NULL,
"percentD" INTEGER NOT NULL,
"percentI" INTEGER NOT NULL,
"percentS" INTEGER NOT NULL,
"percentC" INTEGER NOT NULL,
"validCount" INTEGER NOT NULL,
"primaryTypes" TEXT[] DEFAULT ARRAY[]::TEXT[],
"secondaryTypes" TEXT[] DEFAULT ARRAY[]::TEXT[],
"label" TEXT NOT NULL,
"secondaryLabel" TEXT,
"reliable" BOOLEAN NOT NULL,
"confidence" TEXT NOT NULL,
"note" TEXT,
"status" TEXT NOT NULL DEFAULT 'confirmed',
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" TIMESTAMP(3) NOT NULL,
CONSTRAINT "DiscAssessment_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "SalesCopilotSession" (
"id" TEXT NOT NULL,
"organizationId" TEXT NOT NULL,
"personId" TEXT,
"leadId" TEXT,
"dealId" TEXT,
"userId" TEXT NOT NULL,
"topic" TEXT NOT NULL,
"goal" TEXT NOT NULL,
"customerStatement" TEXT NOT NULL,
"behaviorNotes" TEXT,
"analysis" JSONB NOT NULL,
"confirmedResponse" TEXT,
"nextAction" TEXT,
"followUpAt" TIMESTAMP(3),
"callOutcome" TEXT,
"agentNote" TEXT,
"status" TEXT NOT NULL,
"messageDraft" TEXT,
"activityId" TEXT,
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" TIMESTAMP(3) NOT NULL,
CONSTRAINT "SalesCopilotSession_pkey" PRIMARY KEY ("id")
);
-- CreateIndex
CREATE INDEX "DiscAssessment_organizationId_personId_createdAt_idx" ON "DiscAssessment"("organizationId", "personId", "createdAt");
CREATE INDEX "DiscAssessment_personId_idx" ON "DiscAssessment"("personId");
CREATE INDEX "SalesCopilotSession_organizationId_personId_createdAt_idx" ON "SalesCopilotSession"("organizationId", "personId", "createdAt");
CREATE INDEX "SalesCopilotSession_organizationId_leadId_idx" ON "SalesCopilotSession"("organizationId", "leadId");
CREATE INDEX "SalesCopilotSession_dealId_idx" ON "SalesCopilotSession"("dealId");
-- AddForeignKey
ALTER TABLE "DiscAssessment" ADD CONSTRAINT "DiscAssessment_organizationId_fkey" FOREIGN KEY ("organizationId") REFERENCES "Organization"("id") ON DELETE CASCADE ON UPDATE CASCADE;
ALTER TABLE "DiscAssessment" ADD CONSTRAINT "DiscAssessment_personId_fkey" FOREIGN KEY ("personId") REFERENCES "Person"("id") ON DELETE CASCADE ON UPDATE CASCADE;
ALTER TABLE "DiscAssessment" ADD CONSTRAINT "DiscAssessment_assessorId_fkey" FOREIGN KEY ("assessorId") REFERENCES "User"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
ALTER TABLE "SalesCopilotSession" ADD CONSTRAINT "SalesCopilotSession_organizationId_fkey" FOREIGN KEY ("organizationId") REFERENCES "Organization"("id") ON DELETE CASCADE ON UPDATE CASCADE;
ALTER TABLE "SalesCopilotSession" ADD CONSTRAINT "SalesCopilotSession_personId_fkey" FOREIGN KEY ("personId") REFERENCES "Person"("id") ON DELETE CASCADE ON UPDATE CASCADE;
ALTER TABLE "SalesCopilotSession" ADD CONSTRAINT "SalesCopilotSession_leadId_fkey" FOREIGN KEY ("leadId") REFERENCES "Lead"("id") ON DELETE SET NULL ON UPDATE CASCADE;
ALTER TABLE "SalesCopilotSession" ADD CONSTRAINT "SalesCopilotSession_dealId_fkey" FOREIGN KEY ("dealId") REFERENCES "Deal"("id") ON DELETE SET NULL ON UPDATE CASCADE;
ALTER TABLE "SalesCopilotSession" ADD CONSTRAINT "SalesCopilotSession_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
-- ماژول برای محیط‌هایی که از قبل ماژول دارند روشن می‌شود و در سوپرادمین قابل خاموش کردن است
UPDATE "Organization"
SET "enabledModules" = array_append("enabledModules", 'behavior')
WHERE cardinality("enabledModules") > 0
AND NOT ('behavior' = ANY("enabledModules"));
-- مجوز رفتار برای نقش‌هایی که فروش دارند؛ پشتیبانی بدون این کلید می‌ماند
UPDATE "OrgRole"
SET "permissions" = array_append("permissions", 'behavior')
WHERE NOT ('behavior' = ANY("permissions"))
AND (
"systemKey" IN ('owner', 'admin', 'sales')
OR 'sales' = ANY("permissions")
);