Compare commits

..

No commits in common. "65a22e6f191a0492131c894a5747ae91c0306d5d" and "86fa542371222e821f2663c0490f0478b8c9f172" have entirely different histories.

22 changed files with 553 additions and 1674 deletions

View File

@ -16,143 +16,105 @@
-- the enum. -- the enum.
DO $$ BEGIN ALTER TYPE "SettingCategory" ADD VALUE 'DIGEST'; EXCEPTION WHEN duplicate_object THEN NULL; END $$; ALTER TYPE "SettingCategory" ADD VALUE 'DIGEST';
DO $$ BEGIN ALTER TYPE "SettingCategory" ADD VALUE 'ANALYTICS'; EXCEPTION WHEN duplicate_object THEN NULL; END $$; ALTER TYPE "SettingCategory" ADD VALUE 'ANALYTICS';
DO $$ BEGIN ALTER TYPE "SettingCategory" ADD VALUE 'AUDIT_CONFIG'; EXCEPTION WHEN duplicate_object THEN NULL; END $$; ALTER TYPE "SettingCategory" ADD VALUE 'AUDIT_CONFIG';
DO $$ BEGIN ALTER TYPE "SettingCategory" ADD VALUE 'INTEGRATIONS'; EXCEPTION WHEN duplicate_object THEN NULL; END $$; ALTER TYPE "SettingCategory" ADD VALUE 'INTEGRATIONS';
DO $$ BEGIN ALTER TYPE "SettingCategory" ADD VALUE 'LOCALIZATION'; EXCEPTION WHEN duplicate_object THEN NULL; END $$; ALTER TYPE "SettingCategory" ADD VALUE 'LOCALIZATION';
DO $$ BEGIN ALTER TYPE "SettingCategory" ADD VALUE 'COMMUNICATION'; EXCEPTION WHEN duplicate_object THEN NULL; END $$; ALTER TYPE "SettingCategory" ADD VALUE 'COMMUNICATION';
-- DropForeignKey -- DropForeignKey
ALTER TABLE "ApplicationForm" DROP CONSTRAINT IF EXISTS "ApplicationForm_programId_fkey"; ALTER TABLE "ApplicationForm" DROP CONSTRAINT "ApplicationForm_programId_fkey";
-- DropForeignKey -- DropForeignKey
ALTER TABLE "ApplicationForm" DROP CONSTRAINT IF EXISTS "ApplicationForm_roundId_fkey"; ALTER TABLE "ApplicationForm" DROP CONSTRAINT "ApplicationForm_roundId_fkey";
-- DropForeignKey -- DropForeignKey
ALTER TABLE "ApplicationFormField" DROP CONSTRAINT IF EXISTS "ApplicationFormField_formId_fkey"; ALTER TABLE "ApplicationFormField" DROP CONSTRAINT "ApplicationFormField_formId_fkey";
-- DropForeignKey -- DropForeignKey
ALTER TABLE "ApplicationFormField" DROP CONSTRAINT IF EXISTS "ApplicationFormField_stepId_fkey"; ALTER TABLE "ApplicationFormField" DROP CONSTRAINT "ApplicationFormField_stepId_fkey";
-- DropForeignKey -- DropForeignKey
ALTER TABLE "ApplicationFormSubmission" DROP CONSTRAINT IF EXISTS "ApplicationFormSubmission_formId_fkey"; ALTER TABLE "ApplicationFormSubmission" DROP CONSTRAINT "ApplicationFormSubmission_formId_fkey";
-- DropForeignKey -- DropForeignKey
ALTER TABLE "OnboardingStep" DROP CONSTRAINT IF EXISTS "OnboardingStep_formId_fkey"; ALTER TABLE "OnboardingStep" DROP CONSTRAINT "OnboardingStep_formId_fkey";
-- DropForeignKey -- DropForeignKey
ALTER TABLE "SubmissionFile" DROP CONSTRAINT IF EXISTS "SubmissionFile_submissionId_fkey"; ALTER TABLE "SubmissionFile" DROP CONSTRAINT "SubmissionFile_submissionId_fkey";
-- DropIndex -- DropIndex
DROP INDEX IF EXISTS "User_email_idx"; DROP INDEX "User_email_idx";
-- AlterTable -- AlterTable
DO $$ BEGIN ALTER TABLE "AssignmentJob" ALTER COLUMN "updatedAt" DROP DEFAULT; EXCEPTION WHEN others THEN NULL; END $$; ALTER TABLE "AssignmentJob" ALTER COLUMN "updatedAt" DROP DEFAULT;
-- AlterTable -- AlterTable
DO $$ BEGIN ALTER TABLE "AuditLog" ADD COLUMN "previousDataJson" JSONB,
ALTER TABLE "AuditLog" ADD COLUMN "previousDataJson" JSONB; ADD COLUMN "sessionId" TEXT;
EXCEPTION WHEN duplicate_column THEN NULL; END $$;
DO $$ BEGIN
ALTER TABLE "AuditLog" ADD COLUMN "sessionId" TEXT;
EXCEPTION WHEN duplicate_column THEN NULL; END $$;
-- AlterTable -- AlterTable
DO $$ BEGIN ALTER TABLE "FilteringJob" ALTER COLUMN "updatedAt" DROP DEFAULT; EXCEPTION WHEN others THEN NULL; END $$; ALTER TABLE "FilteringJob" ALTER COLUMN "updatedAt" DROP DEFAULT;
-- AlterTable -- AlterTable
DO $$ BEGIN ALTER TABLE "LiveVote" ADD COLUMN "isAudienceVote" BOOLEAN NOT NULL DEFAULT false;
ALTER TABLE "LiveVote" ADD COLUMN "isAudienceVote" BOOLEAN NOT NULL DEFAULT false;
EXCEPTION WHEN duplicate_column THEN NULL; END $$;
-- AlterTable -- AlterTable
DO $$ BEGIN ALTER TABLE "LiveVotingSession" ADD COLUMN "allowAudienceVotes" BOOLEAN NOT NULL DEFAULT false,
ALTER TABLE "LiveVotingSession" ADD COLUMN "allowAudienceVotes" BOOLEAN NOT NULL DEFAULT false; ADD COLUMN "audienceVoteWeight" DOUBLE PRECISION NOT NULL DEFAULT 0,
EXCEPTION WHEN duplicate_column THEN NULL; END $$; ADD COLUMN "presentationSettingsJson" JSONB,
DO $$ BEGIN ADD COLUMN "tieBreakerMethod" TEXT NOT NULL DEFAULT 'admin_decides';
ALTER TABLE "LiveVotingSession" ADD COLUMN "audienceVoteWeight" DOUBLE PRECISION NOT NULL DEFAULT 0;
EXCEPTION WHEN duplicate_column THEN NULL; END $$;
DO $$ BEGIN
ALTER TABLE "LiveVotingSession" ADD COLUMN "presentationSettingsJson" JSONB;
EXCEPTION WHEN duplicate_column THEN NULL; END $$;
DO $$ BEGIN
ALTER TABLE "LiveVotingSession" ADD COLUMN "tieBreakerMethod" TEXT NOT NULL DEFAULT 'admin_decides';
EXCEPTION WHEN duplicate_column THEN NULL; END $$;
-- AlterTable -- AlterTable
DO $$ BEGIN ALTER TABLE "MentorAssignment" ADD COLUMN "completionStatus" TEXT NOT NULL DEFAULT 'in_progress',
ALTER TABLE "MentorAssignment" ADD COLUMN "completionStatus" TEXT NOT NULL DEFAULT 'in_progress'; ADD COLUMN "lastViewedAt" TIMESTAMP(3);
EXCEPTION WHEN duplicate_column THEN NULL; END $$;
DO $$ BEGIN
ALTER TABLE "MentorAssignment" ADD COLUMN "lastViewedAt" TIMESTAMP(3);
EXCEPTION WHEN duplicate_column THEN NULL; END $$;
-- AlterTable -- AlterTable
DO $$ BEGIN ALTER TABLE "NotificationEmailSetting" ALTER COLUMN "updatedAt" DROP DEFAULT; EXCEPTION WHEN others THEN NULL; END $$; ALTER TABLE "NotificationEmailSetting" ALTER COLUMN "updatedAt" DROP DEFAULT;
-- AlterTable -- AlterTable
DO $$ BEGIN ALTER TABLE "Project" ADD COLUMN "draftDataJson" JSONB,
ALTER TABLE "Project" ADD COLUMN "draftDataJson" JSONB; ADD COLUMN "draftExpiresAt" TIMESTAMP(3),
EXCEPTION WHEN duplicate_column THEN NULL; END $$; ADD COLUMN "isDraft" BOOLEAN NOT NULL DEFAULT false;
DO $$ BEGIN
ALTER TABLE "Project" ADD COLUMN "draftExpiresAt" TIMESTAMP(3);
EXCEPTION WHEN duplicate_column THEN NULL; END $$;
DO $$ BEGIN
ALTER TABLE "Project" ADD COLUMN "isDraft" BOOLEAN NOT NULL DEFAULT false;
EXCEPTION WHEN duplicate_column THEN NULL; END $$;
-- AlterTable -- AlterTable
DO $$ BEGIN ALTER TABLE "ProjectFile" ADD COLUMN "isLate" BOOLEAN NOT NULL DEFAULT false,
ALTER TABLE "ProjectFile" ADD COLUMN "isLate" BOOLEAN NOT NULL DEFAULT false; ADD COLUMN "replacedById" TEXT,
EXCEPTION WHEN duplicate_column THEN NULL; END $$; ADD COLUMN "roundId" TEXT,
DO $$ BEGIN ADD COLUMN "version" INTEGER NOT NULL DEFAULT 1;
ALTER TABLE "ProjectFile" ADD COLUMN "replacedById" TEXT;
EXCEPTION WHEN duplicate_column THEN NULL; END $$;
DO $$ BEGIN
ALTER TABLE "ProjectFile" ADD COLUMN "roundId" TEXT;
EXCEPTION WHEN duplicate_column THEN NULL; END $$;
DO $$ BEGIN
ALTER TABLE "ProjectFile" ADD COLUMN "version" INTEGER NOT NULL DEFAULT 1;
EXCEPTION WHEN duplicate_column THEN NULL; END $$;
-- AlterTable -- AlterTable
DO $$ BEGIN ALTER TABLE "TaggingJob" ALTER COLUMN "updatedAt" DROP DEFAULT; EXCEPTION WHEN others THEN NULL; END $$; ALTER TABLE "TaggingJob" ALTER COLUMN "updatedAt" DROP DEFAULT;
-- AlterTable -- AlterTable
DO $$ BEGIN ALTER TABLE "User" ADD COLUMN "availabilityJson" JSONB,
ALTER TABLE "User" ADD COLUMN "availabilityJson" JSONB; ADD COLUMN "digestFrequency" TEXT NOT NULL DEFAULT 'none',
EXCEPTION WHEN duplicate_column THEN NULL; END $$; ADD COLUMN "preferredWorkload" INTEGER;
DO $$ BEGIN
ALTER TABLE "User" ADD COLUMN "digestFrequency" TEXT NOT NULL DEFAULT 'none';
EXCEPTION WHEN duplicate_column THEN NULL; END $$;
DO $$ BEGIN
ALTER TABLE "User" ADD COLUMN "preferredWorkload" INTEGER;
EXCEPTION WHEN duplicate_column THEN NULL; END $$;
-- DropTable -- DropTable
DROP TABLE IF EXISTS "ApplicationForm"; DROP TABLE "ApplicationForm";
-- DropTable -- DropTable
DROP TABLE IF EXISTS "ApplicationFormField"; DROP TABLE "ApplicationFormField";
-- DropTable -- DropTable
DROP TABLE IF EXISTS "ApplicationFormSubmission"; DROP TABLE "ApplicationFormSubmission";
-- DropTable -- DropTable
DROP TABLE IF EXISTS "OnboardingStep"; DROP TABLE "OnboardingStep";
-- DropTable -- DropTable
DROP TABLE IF EXISTS "SubmissionFile"; DROP TABLE "SubmissionFile";
-- DropEnum -- DropEnum
DROP TYPE IF EXISTS "FormFieldType"; DROP TYPE "FormFieldType";
-- DropEnum -- DropEnum
DROP TYPE IF EXISTS "SpecialFieldType"; DROP TYPE "SpecialFieldType";
-- CreateTable -- CreateTable
CREATE TABLE IF NOT EXISTS "ReminderLog" ( CREATE TABLE "ReminderLog" (
"id" TEXT NOT NULL, "id" TEXT NOT NULL,
"roundId" TEXT NOT NULL, "roundId" TEXT NOT NULL,
"userId" TEXT NOT NULL, "userId" TEXT NOT NULL,
@ -163,7 +125,7 @@ CREATE TABLE IF NOT EXISTS "ReminderLog" (
); );
-- CreateTable -- CreateTable
CREATE TABLE IF NOT EXISTS "ConflictOfInterest" ( CREATE TABLE "ConflictOfInterest" (
"id" TEXT NOT NULL, "id" TEXT NOT NULL,
"assignmentId" TEXT NOT NULL, "assignmentId" TEXT NOT NULL,
"userId" TEXT NOT NULL, "userId" TEXT NOT NULL,
@ -181,7 +143,7 @@ CREATE TABLE IF NOT EXISTS "ConflictOfInterest" (
); );
-- CreateTable -- CreateTable
CREATE TABLE IF NOT EXISTS "EvaluationSummary" ( CREATE TABLE "EvaluationSummary" (
"id" TEXT NOT NULL, "id" TEXT NOT NULL,
"projectId" TEXT NOT NULL, "projectId" TEXT NOT NULL,
"roundId" TEXT NOT NULL, "roundId" TEXT NOT NULL,
@ -195,7 +157,7 @@ CREATE TABLE IF NOT EXISTS "EvaluationSummary" (
); );
-- CreateTable -- CreateTable
CREATE TABLE IF NOT EXISTS "ProjectStatusHistory" ( CREATE TABLE "ProjectStatusHistory" (
"id" TEXT NOT NULL, "id" TEXT NOT NULL,
"projectId" TEXT NOT NULL, "projectId" TEXT NOT NULL,
"status" "ProjectStatus" NOT NULL, "status" "ProjectStatus" NOT NULL,
@ -206,7 +168,7 @@ CREATE TABLE IF NOT EXISTS "ProjectStatusHistory" (
); );
-- CreateTable -- CreateTable
CREATE TABLE IF NOT EXISTS "MentorMessage" ( CREATE TABLE "MentorMessage" (
"id" TEXT NOT NULL, "id" TEXT NOT NULL,
"projectId" TEXT NOT NULL, "projectId" TEXT NOT NULL,
"senderId" TEXT NOT NULL, "senderId" TEXT NOT NULL,
@ -218,7 +180,7 @@ CREATE TABLE IF NOT EXISTS "MentorMessage" (
); );
-- CreateTable -- CreateTable
CREATE TABLE IF NOT EXISTS "DigestLog" ( CREATE TABLE "DigestLog" (
"id" TEXT NOT NULL, "id" TEXT NOT NULL,
"userId" TEXT NOT NULL, "userId" TEXT NOT NULL,
"digestType" TEXT NOT NULL, "digestType" TEXT NOT NULL,
@ -229,7 +191,7 @@ CREATE TABLE IF NOT EXISTS "DigestLog" (
); );
-- CreateTable -- CreateTable
CREATE TABLE IF NOT EXISTS "RoundTemplate" ( CREATE TABLE "RoundTemplate" (
"id" TEXT NOT NULL, "id" TEXT NOT NULL,
"name" TEXT NOT NULL, "name" TEXT NOT NULL,
"description" TEXT, "description" TEXT,
@ -246,7 +208,7 @@ CREATE TABLE IF NOT EXISTS "RoundTemplate" (
); );
-- CreateTable -- CreateTable
CREATE TABLE IF NOT EXISTS "MentorNote" ( CREATE TABLE "MentorNote" (
"id" TEXT NOT NULL, "id" TEXT NOT NULL,
"mentorAssignmentId" TEXT NOT NULL, "mentorAssignmentId" TEXT NOT NULL,
"authorId" TEXT NOT NULL, "authorId" TEXT NOT NULL,
@ -259,7 +221,7 @@ CREATE TABLE IF NOT EXISTS "MentorNote" (
); );
-- CreateTable -- CreateTable
CREATE TABLE IF NOT EXISTS "MentorMilestone" ( CREATE TABLE "MentorMilestone" (
"id" TEXT NOT NULL, "id" TEXT NOT NULL,
"programId" TEXT NOT NULL, "programId" TEXT NOT NULL,
"name" TEXT NOT NULL, "name" TEXT NOT NULL,
@ -274,7 +236,7 @@ CREATE TABLE IF NOT EXISTS "MentorMilestone" (
); );
-- CreateTable -- CreateTable
CREATE TABLE IF NOT EXISTS "MentorMilestoneCompletion" ( CREATE TABLE "MentorMilestoneCompletion" (
"id" TEXT NOT NULL, "id" TEXT NOT NULL,
"milestoneId" TEXT NOT NULL, "milestoneId" TEXT NOT NULL,
"mentorAssignmentId" TEXT NOT NULL, "mentorAssignmentId" TEXT NOT NULL,
@ -285,7 +247,7 @@ CREATE TABLE IF NOT EXISTS "MentorMilestoneCompletion" (
); );
-- CreateTable -- CreateTable
CREATE TABLE IF NOT EXISTS "Message" ( CREATE TABLE "Message" (
"id" TEXT NOT NULL, "id" TEXT NOT NULL,
"senderId" TEXT NOT NULL, "senderId" TEXT NOT NULL,
"recipientType" TEXT NOT NULL, "recipientType" TEXT NOT NULL,
@ -304,7 +266,7 @@ CREATE TABLE IF NOT EXISTS "Message" (
); );
-- CreateTable -- CreateTable
CREATE TABLE IF NOT EXISTS "MessageTemplate" ( CREATE TABLE "MessageTemplate" (
"id" TEXT NOT NULL, "id" TEXT NOT NULL,
"name" TEXT NOT NULL, "name" TEXT NOT NULL,
"category" TEXT NOT NULL, "category" TEXT NOT NULL,
@ -320,7 +282,7 @@ CREATE TABLE IF NOT EXISTS "MessageTemplate" (
); );
-- CreateTable -- CreateTable
CREATE TABLE IF NOT EXISTS "MessageRecipient" ( CREATE TABLE "MessageRecipient" (
"id" TEXT NOT NULL, "id" TEXT NOT NULL,
"messageId" TEXT NOT NULL, "messageId" TEXT NOT NULL,
"userId" TEXT NOT NULL, "userId" TEXT NOT NULL,
@ -333,7 +295,7 @@ CREATE TABLE IF NOT EXISTS "MessageRecipient" (
); );
-- CreateTable -- CreateTable
CREATE TABLE IF NOT EXISTS "Webhook" ( CREATE TABLE "Webhook" (
"id" TEXT NOT NULL, "id" TEXT NOT NULL,
"name" TEXT NOT NULL, "name" TEXT NOT NULL,
"url" TEXT NOT NULL, "url" TEXT NOT NULL,
@ -350,7 +312,7 @@ CREATE TABLE IF NOT EXISTS "Webhook" (
); );
-- CreateTable -- CreateTable
CREATE TABLE IF NOT EXISTS "WebhookDelivery" ( CREATE TABLE "WebhookDelivery" (
"id" TEXT NOT NULL, "id" TEXT NOT NULL,
"webhookId" TEXT NOT NULL, "webhookId" TEXT NOT NULL,
"event" TEXT NOT NULL, "event" TEXT NOT NULL,
@ -366,7 +328,7 @@ CREATE TABLE IF NOT EXISTS "WebhookDelivery" (
); );
-- CreateTable -- CreateTable
CREATE TABLE IF NOT EXISTS "EvaluationDiscussion" ( CREATE TABLE "EvaluationDiscussion" (
"id" TEXT NOT NULL, "id" TEXT NOT NULL,
"projectId" TEXT NOT NULL, "projectId" TEXT NOT NULL,
"roundId" TEXT NOT NULL, "roundId" TEXT NOT NULL,
@ -379,7 +341,7 @@ CREATE TABLE IF NOT EXISTS "EvaluationDiscussion" (
); );
-- CreateTable -- CreateTable
CREATE TABLE IF NOT EXISTS "DiscussionComment" ( CREATE TABLE "DiscussionComment" (
"id" TEXT NOT NULL, "id" TEXT NOT NULL,
"discussionId" TEXT NOT NULL, "discussionId" TEXT NOT NULL,
"userId" TEXT NOT NULL, "userId" TEXT NOT NULL,
@ -390,257 +352,199 @@ CREATE TABLE IF NOT EXISTS "DiscussionComment" (
); );
-- CreateIndex -- CreateIndex
CREATE INDEX IF NOT EXISTS "ReminderLog_roundId_idx" ON "ReminderLog"("roundId"); CREATE INDEX "ReminderLog_roundId_idx" ON "ReminderLog"("roundId");
-- CreateIndex -- CreateIndex
CREATE UNIQUE INDEX IF NOT EXISTS "ReminderLog_roundId_userId_type_key" ON "ReminderLog"("roundId", "userId", "type"); CREATE UNIQUE INDEX "ReminderLog_roundId_userId_type_key" ON "ReminderLog"("roundId", "userId", "type");
-- CreateIndex -- CreateIndex
CREATE UNIQUE INDEX IF NOT EXISTS "ConflictOfInterest_assignmentId_key" ON "ConflictOfInterest"("assignmentId"); CREATE UNIQUE INDEX "ConflictOfInterest_assignmentId_key" ON "ConflictOfInterest"("assignmentId");
-- CreateIndex -- CreateIndex
CREATE INDEX IF NOT EXISTS "ConflictOfInterest_userId_idx" ON "ConflictOfInterest"("userId"); CREATE INDEX "ConflictOfInterest_userId_idx" ON "ConflictOfInterest"("userId");
-- CreateIndex -- CreateIndex
CREATE INDEX IF NOT EXISTS "ConflictOfInterest_roundId_hasConflict_idx" ON "ConflictOfInterest"("roundId", "hasConflict"); CREATE INDEX "ConflictOfInterest_roundId_hasConflict_idx" ON "ConflictOfInterest"("roundId", "hasConflict");
-- CreateIndex -- CreateIndex
CREATE INDEX IF NOT EXISTS "EvaluationSummary_roundId_idx" ON "EvaluationSummary"("roundId"); CREATE INDEX "EvaluationSummary_roundId_idx" ON "EvaluationSummary"("roundId");
-- CreateIndex -- CreateIndex
CREATE UNIQUE INDEX IF NOT EXISTS "EvaluationSummary_projectId_roundId_key" ON "EvaluationSummary"("projectId", "roundId"); CREATE UNIQUE INDEX "EvaluationSummary_projectId_roundId_key" ON "EvaluationSummary"("projectId", "roundId");
-- CreateIndex -- CreateIndex
CREATE INDEX IF NOT EXISTS "ProjectStatusHistory_projectId_changedAt_idx" ON "ProjectStatusHistory"("projectId", "changedAt"); CREATE INDEX "ProjectStatusHistory_projectId_changedAt_idx" ON "ProjectStatusHistory"("projectId", "changedAt");
-- CreateIndex -- CreateIndex
CREATE INDEX IF NOT EXISTS "MentorMessage_projectId_createdAt_idx" ON "MentorMessage"("projectId", "createdAt"); CREATE INDEX "MentorMessage_projectId_createdAt_idx" ON "MentorMessage"("projectId", "createdAt");
-- CreateIndex -- CreateIndex
CREATE INDEX IF NOT EXISTS "DigestLog_userId_idx" ON "DigestLog"("userId"); CREATE INDEX "DigestLog_userId_idx" ON "DigestLog"("userId");
-- CreateIndex -- CreateIndex
CREATE INDEX IF NOT EXISTS "DigestLog_sentAt_idx" ON "DigestLog"("sentAt"); CREATE INDEX "DigestLog_sentAt_idx" ON "DigestLog"("sentAt");
-- CreateIndex -- CreateIndex
CREATE INDEX IF NOT EXISTS "RoundTemplate_programId_idx" ON "RoundTemplate"("programId"); CREATE INDEX "RoundTemplate_programId_idx" ON "RoundTemplate"("programId");
-- CreateIndex -- CreateIndex
CREATE INDEX IF NOT EXISTS "MentorNote_mentorAssignmentId_idx" ON "MentorNote"("mentorAssignmentId"); CREATE INDEX "MentorNote_mentorAssignmentId_idx" ON "MentorNote"("mentorAssignmentId");
-- CreateIndex -- CreateIndex
CREATE INDEX IF NOT EXISTS "MentorMilestone_programId_idx" ON "MentorMilestone"("programId"); CREATE INDEX "MentorMilestone_programId_idx" ON "MentorMilestone"("programId");
-- CreateIndex -- CreateIndex
CREATE INDEX IF NOT EXISTS "MentorMilestone_sortOrder_idx" ON "MentorMilestone"("sortOrder"); CREATE INDEX "MentorMilestone_sortOrder_idx" ON "MentorMilestone"("sortOrder");
-- CreateIndex -- CreateIndex
CREATE INDEX IF NOT EXISTS "MentorMilestoneCompletion_mentorAssignmentId_idx" ON "MentorMilestoneCompletion"("mentorAssignmentId"); CREATE INDEX "MentorMilestoneCompletion_mentorAssignmentId_idx" ON "MentorMilestoneCompletion"("mentorAssignmentId");
-- CreateIndex -- CreateIndex
CREATE UNIQUE INDEX IF NOT EXISTS "MentorMilestoneCompletion_milestoneId_mentorAssignmentId_key" ON "MentorMilestoneCompletion"("milestoneId", "mentorAssignmentId"); CREATE UNIQUE INDEX "MentorMilestoneCompletion_milestoneId_mentorAssignmentId_key" ON "MentorMilestoneCompletion"("milestoneId", "mentorAssignmentId");
-- CreateIndex -- CreateIndex
CREATE INDEX IF NOT EXISTS "Message_senderId_idx" ON "Message"("senderId"); CREATE INDEX "Message_senderId_idx" ON "Message"("senderId");
-- CreateIndex -- CreateIndex
CREATE INDEX IF NOT EXISTS "Message_sentAt_idx" ON "Message"("sentAt"); CREATE INDEX "Message_sentAt_idx" ON "Message"("sentAt");
-- CreateIndex -- CreateIndex
CREATE INDEX IF NOT EXISTS "Message_scheduledAt_idx" ON "Message"("scheduledAt"); CREATE INDEX "Message_scheduledAt_idx" ON "Message"("scheduledAt");
-- CreateIndex -- CreateIndex
CREATE INDEX IF NOT EXISTS "MessageTemplate_category_idx" ON "MessageTemplate"("category"); CREATE INDEX "MessageTemplate_category_idx" ON "MessageTemplate"("category");
-- CreateIndex -- CreateIndex
CREATE INDEX IF NOT EXISTS "MessageTemplate_isActive_idx" ON "MessageTemplate"("isActive"); CREATE INDEX "MessageTemplate_isActive_idx" ON "MessageTemplate"("isActive");
-- CreateIndex -- CreateIndex
CREATE INDEX IF NOT EXISTS "MessageRecipient_messageId_idx" ON "MessageRecipient"("messageId"); CREATE INDEX "MessageRecipient_messageId_idx" ON "MessageRecipient"("messageId");
-- CreateIndex -- CreateIndex
CREATE INDEX IF NOT EXISTS "MessageRecipient_userId_isRead_idx" ON "MessageRecipient"("userId", "isRead"); CREATE INDEX "MessageRecipient_userId_isRead_idx" ON "MessageRecipient"("userId", "isRead");
-- CreateIndex -- CreateIndex
CREATE INDEX IF NOT EXISTS "Webhook_isActive_idx" ON "Webhook"("isActive"); CREATE INDEX "Webhook_isActive_idx" ON "Webhook"("isActive");
-- CreateIndex -- CreateIndex
CREATE INDEX IF NOT EXISTS "WebhookDelivery_webhookId_idx" ON "WebhookDelivery"("webhookId"); CREATE INDEX "WebhookDelivery_webhookId_idx" ON "WebhookDelivery"("webhookId");
-- CreateIndex -- CreateIndex
CREATE INDEX IF NOT EXISTS "WebhookDelivery_status_idx" ON "WebhookDelivery"("status"); CREATE INDEX "WebhookDelivery_status_idx" ON "WebhookDelivery"("status");
-- CreateIndex -- CreateIndex
CREATE INDEX IF NOT EXISTS "WebhookDelivery_createdAt_idx" ON "WebhookDelivery"("createdAt"); CREATE INDEX "WebhookDelivery_createdAt_idx" ON "WebhookDelivery"("createdAt");
-- CreateIndex -- CreateIndex
CREATE INDEX IF NOT EXISTS "EvaluationDiscussion_roundId_idx" ON "EvaluationDiscussion"("roundId"); CREATE INDEX "EvaluationDiscussion_roundId_idx" ON "EvaluationDiscussion"("roundId");
-- CreateIndex -- CreateIndex
CREATE INDEX IF NOT EXISTS "EvaluationDiscussion_status_idx" ON "EvaluationDiscussion"("status"); CREATE INDEX "EvaluationDiscussion_status_idx" ON "EvaluationDiscussion"("status");
-- CreateIndex -- CreateIndex
CREATE UNIQUE INDEX IF NOT EXISTS "EvaluationDiscussion_projectId_roundId_key" ON "EvaluationDiscussion"("projectId", "roundId"); CREATE UNIQUE INDEX "EvaluationDiscussion_projectId_roundId_key" ON "EvaluationDiscussion"("projectId", "roundId");
-- CreateIndex -- CreateIndex
CREATE INDEX IF NOT EXISTS "DiscussionComment_discussionId_createdAt_idx" ON "DiscussionComment"("discussionId", "createdAt"); CREATE INDEX "DiscussionComment_discussionId_createdAt_idx" ON "DiscussionComment"("discussionId", "createdAt");
-- CreateIndex -- CreateIndex
CREATE INDEX IF NOT EXISTS "AuditLog_entityType_entityId_timestamp_idx" ON "AuditLog"("entityType", "entityId", "timestamp"); CREATE INDEX "AuditLog_entityType_entityId_timestamp_idx" ON "AuditLog"("entityType", "entityId", "timestamp");
-- CreateIndex -- CreateIndex
CREATE INDEX IF NOT EXISTS "Evaluation_status_formId_idx" ON "Evaluation"("status", "formId"); CREATE INDEX "Evaluation_status_formId_idx" ON "Evaluation"("status", "formId");
-- CreateIndex -- CreateIndex
CREATE INDEX IF NOT EXISTS "GracePeriod_roundId_userId_extendedUntil_idx" ON "GracePeriod"("roundId", "userId", "extendedUntil"); CREATE INDEX "GracePeriod_roundId_userId_extendedUntil_idx" ON "GracePeriod"("roundId", "userId", "extendedUntil");
-- CreateIndex -- CreateIndex
CREATE INDEX IF NOT EXISTS "LiveVote_isAudienceVote_idx" ON "LiveVote"("isAudienceVote"); CREATE INDEX "LiveVote_isAudienceVote_idx" ON "LiveVote"("isAudienceVote");
-- CreateIndex -- CreateIndex
CREATE INDEX IF NOT EXISTS "ProjectFile_roundId_idx" ON "ProjectFile"("roundId"); CREATE INDEX "ProjectFile_roundId_idx" ON "ProjectFile"("roundId");
-- AddForeignKey -- AddForeignKey
DO $$ BEGIN ALTER TABLE "ProjectFile" ADD CONSTRAINT "ProjectFile_roundId_fkey" FOREIGN KEY ("roundId") REFERENCES "Round"("id") ON DELETE SET NULL ON UPDATE CASCADE;
ALTER TABLE "ProjectFile" ADD CONSTRAINT "ProjectFile_roundId_fkey" FOREIGN KEY ("roundId") REFERENCES "Round"("id") ON DELETE SET NULL ON UPDATE CASCADE;
EXCEPTION WHEN duplicate_object THEN NULL; END $$;
-- AddForeignKey -- AddForeignKey
DO $$ BEGIN ALTER TABLE "SpecialAward" ADD CONSTRAINT "SpecialAward_winnerOverriddenBy_fkey" FOREIGN KEY ("winnerOverriddenBy") REFERENCES "User"("id") ON DELETE SET NULL ON UPDATE CASCADE;
ALTER TABLE "SpecialAward" ADD CONSTRAINT "SpecialAward_winnerOverriddenBy_fkey" FOREIGN KEY ("winnerOverriddenBy") REFERENCES "User"("id") ON DELETE SET NULL ON UPDATE CASCADE;
EXCEPTION WHEN duplicate_object THEN NULL; END $$;
-- AddForeignKey -- AddForeignKey
DO $$ BEGIN ALTER TABLE "ReminderLog" ADD CONSTRAINT "ReminderLog_roundId_fkey" FOREIGN KEY ("roundId") REFERENCES "Round"("id") ON DELETE CASCADE ON UPDATE CASCADE;
ALTER TABLE "ReminderLog" ADD CONSTRAINT "ReminderLog_roundId_fkey" FOREIGN KEY ("roundId") REFERENCES "Round"("id") ON DELETE CASCADE ON UPDATE CASCADE;
EXCEPTION WHEN duplicate_object THEN NULL; END $$;
-- AddForeignKey -- AddForeignKey
DO $$ BEGIN ALTER TABLE "ReminderLog" ADD CONSTRAINT "ReminderLog_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User"("id") ON DELETE CASCADE ON UPDATE CASCADE;
ALTER TABLE "ReminderLog" ADD CONSTRAINT "ReminderLog_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User"("id") ON DELETE CASCADE ON UPDATE CASCADE;
EXCEPTION WHEN duplicate_object THEN NULL; END $$;
-- AddForeignKey -- AddForeignKey
DO $$ BEGIN ALTER TABLE "ConflictOfInterest" ADD CONSTRAINT "ConflictOfInterest_assignmentId_fkey" FOREIGN KEY ("assignmentId") REFERENCES "Assignment"("id") ON DELETE CASCADE ON UPDATE CASCADE;
ALTER TABLE "ConflictOfInterest" ADD CONSTRAINT "ConflictOfInterest_assignmentId_fkey" FOREIGN KEY ("assignmentId") REFERENCES "Assignment"("id") ON DELETE CASCADE ON UPDATE CASCADE;
EXCEPTION WHEN duplicate_object THEN NULL; END $$;
-- AddForeignKey -- AddForeignKey
DO $$ BEGIN ALTER TABLE "ConflictOfInterest" ADD CONSTRAINT "ConflictOfInterest_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
ALTER TABLE "ConflictOfInterest" ADD CONSTRAINT "ConflictOfInterest_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
EXCEPTION WHEN duplicate_object THEN NULL; END $$;
-- AddForeignKey -- AddForeignKey
DO $$ BEGIN ALTER TABLE "ConflictOfInterest" ADD CONSTRAINT "ConflictOfInterest_reviewedById_fkey" FOREIGN KEY ("reviewedById") REFERENCES "User"("id") ON DELETE SET NULL ON UPDATE CASCADE;
ALTER TABLE "ConflictOfInterest" ADD CONSTRAINT "ConflictOfInterest_reviewedById_fkey" FOREIGN KEY ("reviewedById") REFERENCES "User"("id") ON DELETE SET NULL ON UPDATE CASCADE;
EXCEPTION WHEN duplicate_object THEN NULL; END $$;
-- AddForeignKey -- AddForeignKey
DO $$ BEGIN ALTER TABLE "EvaluationSummary" ADD CONSTRAINT "EvaluationSummary_projectId_fkey" FOREIGN KEY ("projectId") REFERENCES "Project"("id") ON DELETE CASCADE ON UPDATE CASCADE;
ALTER TABLE "EvaluationSummary" ADD CONSTRAINT "EvaluationSummary_projectId_fkey" FOREIGN KEY ("projectId") REFERENCES "Project"("id") ON DELETE CASCADE ON UPDATE CASCADE;
EXCEPTION WHEN duplicate_object THEN NULL; END $$;
-- AddForeignKey -- AddForeignKey
DO $$ BEGIN ALTER TABLE "EvaluationSummary" ADD CONSTRAINT "EvaluationSummary_generatedById_fkey" FOREIGN KEY ("generatedById") REFERENCES "User"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
ALTER TABLE "EvaluationSummary" ADD CONSTRAINT "EvaluationSummary_generatedById_fkey" FOREIGN KEY ("generatedById") REFERENCES "User"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
EXCEPTION WHEN duplicate_object THEN NULL; END $$;
-- AddForeignKey -- AddForeignKey
DO $$ BEGIN ALTER TABLE "ProjectStatusHistory" ADD CONSTRAINT "ProjectStatusHistory_projectId_fkey" FOREIGN KEY ("projectId") REFERENCES "Project"("id") ON DELETE CASCADE ON UPDATE CASCADE;
ALTER TABLE "ProjectStatusHistory" ADD CONSTRAINT "ProjectStatusHistory_projectId_fkey" FOREIGN KEY ("projectId") REFERENCES "Project"("id") ON DELETE CASCADE ON UPDATE CASCADE;
EXCEPTION WHEN duplicate_object THEN NULL; END $$;
-- AddForeignKey -- AddForeignKey
DO $$ BEGIN ALTER TABLE "MentorMessage" ADD CONSTRAINT "MentorMessage_projectId_fkey" FOREIGN KEY ("projectId") REFERENCES "Project"("id") ON DELETE CASCADE ON UPDATE CASCADE;
ALTER TABLE "MentorMessage" ADD CONSTRAINT "MentorMessage_projectId_fkey" FOREIGN KEY ("projectId") REFERENCES "Project"("id") ON DELETE CASCADE ON UPDATE CASCADE;
EXCEPTION WHEN duplicate_object THEN NULL; END $$;
-- AddForeignKey -- AddForeignKey
DO $$ BEGIN ALTER TABLE "MentorMessage" ADD CONSTRAINT "MentorMessage_senderId_fkey" FOREIGN KEY ("senderId") REFERENCES "User"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
ALTER TABLE "MentorMessage" ADD CONSTRAINT "MentorMessage_senderId_fkey" FOREIGN KEY ("senderId") REFERENCES "User"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
EXCEPTION WHEN duplicate_object THEN NULL; END $$;
-- AddForeignKey -- AddForeignKey
DO $$ BEGIN ALTER TABLE "DigestLog" ADD CONSTRAINT "DigestLog_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User"("id") ON DELETE CASCADE ON UPDATE CASCADE;
ALTER TABLE "DigestLog" ADD CONSTRAINT "DigestLog_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User"("id") ON DELETE CASCADE ON UPDATE CASCADE;
EXCEPTION WHEN duplicate_object THEN NULL; END $$;
-- AddForeignKey -- AddForeignKey
DO $$ BEGIN ALTER TABLE "MentorNote" ADD CONSTRAINT "MentorNote_mentorAssignmentId_fkey" FOREIGN KEY ("mentorAssignmentId") REFERENCES "MentorAssignment"("id") ON DELETE CASCADE ON UPDATE CASCADE;
ALTER TABLE "MentorNote" ADD CONSTRAINT "MentorNote_mentorAssignmentId_fkey" FOREIGN KEY ("mentorAssignmentId") REFERENCES "MentorAssignment"("id") ON DELETE CASCADE ON UPDATE CASCADE;
EXCEPTION WHEN duplicate_object THEN NULL; END $$;
-- AddForeignKey -- AddForeignKey
DO $$ BEGIN ALTER TABLE "MentorNote" ADD CONSTRAINT "MentorNote_authorId_fkey" FOREIGN KEY ("authorId") REFERENCES "User"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
ALTER TABLE "MentorNote" ADD CONSTRAINT "MentorNote_authorId_fkey" FOREIGN KEY ("authorId") REFERENCES "User"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
EXCEPTION WHEN duplicate_object THEN NULL; END $$;
-- AddForeignKey -- AddForeignKey
DO $$ BEGIN ALTER TABLE "MentorMilestone" ADD CONSTRAINT "MentorMilestone_programId_fkey" FOREIGN KEY ("programId") REFERENCES "Program"("id") ON DELETE CASCADE ON UPDATE CASCADE;
ALTER TABLE "MentorMilestone" ADD CONSTRAINT "MentorMilestone_programId_fkey" FOREIGN KEY ("programId") REFERENCES "Program"("id") ON DELETE CASCADE ON UPDATE CASCADE;
EXCEPTION WHEN duplicate_object THEN NULL; END $$;
-- AddForeignKey -- AddForeignKey
DO $$ BEGIN ALTER TABLE "MentorMilestoneCompletion" ADD CONSTRAINT "MentorMilestoneCompletion_milestoneId_fkey" FOREIGN KEY ("milestoneId") REFERENCES "MentorMilestone"("id") ON DELETE CASCADE ON UPDATE CASCADE;
ALTER TABLE "MentorMilestoneCompletion" ADD CONSTRAINT "MentorMilestoneCompletion_milestoneId_fkey" FOREIGN KEY ("milestoneId") REFERENCES "MentorMilestone"("id") ON DELETE CASCADE ON UPDATE CASCADE;
EXCEPTION WHEN duplicate_object THEN NULL; END $$;
-- AddForeignKey -- AddForeignKey
DO $$ BEGIN ALTER TABLE "MentorMilestoneCompletion" ADD CONSTRAINT "MentorMilestoneCompletion_mentorAssignmentId_fkey" FOREIGN KEY ("mentorAssignmentId") REFERENCES "MentorAssignment"("id") ON DELETE CASCADE ON UPDATE CASCADE;
ALTER TABLE "MentorMilestoneCompletion" ADD CONSTRAINT "MentorMilestoneCompletion_mentorAssignmentId_fkey" FOREIGN KEY ("mentorAssignmentId") REFERENCES "MentorAssignment"("id") ON DELETE CASCADE ON UPDATE CASCADE;
EXCEPTION WHEN duplicate_object THEN NULL; END $$;
-- AddForeignKey -- AddForeignKey
DO $$ BEGIN ALTER TABLE "MentorMilestoneCompletion" ADD CONSTRAINT "MentorMilestoneCompletion_completedById_fkey" FOREIGN KEY ("completedById") REFERENCES "User"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
ALTER TABLE "MentorMilestoneCompletion" ADD CONSTRAINT "MentorMilestoneCompletion_completedById_fkey" FOREIGN KEY ("completedById") REFERENCES "User"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
EXCEPTION WHEN duplicate_object THEN NULL; END $$;
-- AddForeignKey -- AddForeignKey
DO $$ BEGIN ALTER TABLE "Message" ADD CONSTRAINT "Message_senderId_fkey" FOREIGN KEY ("senderId") REFERENCES "User"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
ALTER TABLE "Message" ADD CONSTRAINT "Message_senderId_fkey" FOREIGN KEY ("senderId") REFERENCES "User"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
EXCEPTION WHEN duplicate_object THEN NULL; END $$;
-- AddForeignKey -- AddForeignKey
DO $$ BEGIN ALTER TABLE "Message" ADD CONSTRAINT "Message_templateId_fkey" FOREIGN KEY ("templateId") REFERENCES "MessageTemplate"("id") ON DELETE SET NULL ON UPDATE CASCADE;
ALTER TABLE "Message" ADD CONSTRAINT "Message_templateId_fkey" FOREIGN KEY ("templateId") REFERENCES "MessageTemplate"("id") ON DELETE SET NULL ON UPDATE CASCADE;
EXCEPTION WHEN duplicate_object THEN NULL; END $$;
-- AddForeignKey -- AddForeignKey
DO $$ BEGIN ALTER TABLE "MessageRecipient" ADD CONSTRAINT "MessageRecipient_messageId_fkey" FOREIGN KEY ("messageId") REFERENCES "Message"("id") ON DELETE CASCADE ON UPDATE CASCADE;
ALTER TABLE "MessageRecipient" ADD CONSTRAINT "MessageRecipient_messageId_fkey" FOREIGN KEY ("messageId") REFERENCES "Message"("id") ON DELETE CASCADE ON UPDATE CASCADE;
EXCEPTION WHEN duplicate_object THEN NULL; END $$;
-- AddForeignKey -- AddForeignKey
DO $$ BEGIN ALTER TABLE "MessageRecipient" ADD CONSTRAINT "MessageRecipient_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User"("id") ON DELETE CASCADE ON UPDATE CASCADE;
ALTER TABLE "MessageRecipient" ADD CONSTRAINT "MessageRecipient_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User"("id") ON DELETE CASCADE ON UPDATE CASCADE;
EXCEPTION WHEN duplicate_object THEN NULL; END $$;
-- AddForeignKey -- AddForeignKey
DO $$ BEGIN ALTER TABLE "Webhook" ADD CONSTRAINT "Webhook_createdById_fkey" FOREIGN KEY ("createdById") REFERENCES "User"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
ALTER TABLE "Webhook" ADD CONSTRAINT "Webhook_createdById_fkey" FOREIGN KEY ("createdById") REFERENCES "User"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
EXCEPTION WHEN duplicate_object THEN NULL; END $$;
-- AddForeignKey -- AddForeignKey
DO $$ BEGIN ALTER TABLE "WebhookDelivery" ADD CONSTRAINT "WebhookDelivery_webhookId_fkey" FOREIGN KEY ("webhookId") REFERENCES "Webhook"("id") ON DELETE CASCADE ON UPDATE CASCADE;
ALTER TABLE "WebhookDelivery" ADD CONSTRAINT "WebhookDelivery_webhookId_fkey" FOREIGN KEY ("webhookId") REFERENCES "Webhook"("id") ON DELETE CASCADE ON UPDATE CASCADE;
EXCEPTION WHEN duplicate_object THEN NULL; END $$;
-- AddForeignKey -- AddForeignKey
DO $$ BEGIN ALTER TABLE "EvaluationDiscussion" ADD CONSTRAINT "EvaluationDiscussion_projectId_fkey" FOREIGN KEY ("projectId") REFERENCES "Project"("id") ON DELETE CASCADE ON UPDATE CASCADE;
ALTER TABLE "EvaluationDiscussion" ADD CONSTRAINT "EvaluationDiscussion_projectId_fkey" FOREIGN KEY ("projectId") REFERENCES "Project"("id") ON DELETE CASCADE ON UPDATE CASCADE;
EXCEPTION WHEN duplicate_object THEN NULL; END $$;
-- AddForeignKey -- AddForeignKey
DO $$ BEGIN ALTER TABLE "EvaluationDiscussion" ADD CONSTRAINT "EvaluationDiscussion_closedById_fkey" FOREIGN KEY ("closedById") REFERENCES "User"("id") ON DELETE SET NULL ON UPDATE CASCADE;
ALTER TABLE "EvaluationDiscussion" ADD CONSTRAINT "EvaluationDiscussion_closedById_fkey" FOREIGN KEY ("closedById") REFERENCES "User"("id") ON DELETE SET NULL ON UPDATE CASCADE;
EXCEPTION WHEN duplicate_object THEN NULL; END $$;
-- AddForeignKey -- AddForeignKey
DO $$ BEGIN ALTER TABLE "DiscussionComment" ADD CONSTRAINT "DiscussionComment_discussionId_fkey" FOREIGN KEY ("discussionId") REFERENCES "EvaluationDiscussion"("id") ON DELETE CASCADE ON UPDATE CASCADE;
ALTER TABLE "DiscussionComment" ADD CONSTRAINT "DiscussionComment_discussionId_fkey" FOREIGN KEY ("discussionId") REFERENCES "EvaluationDiscussion"("id") ON DELETE CASCADE ON UPDATE CASCADE;
EXCEPTION WHEN duplicate_object THEN NULL; END $$;
-- AddForeignKey -- AddForeignKey
DO $$ BEGIN ALTER TABLE "DiscussionComment" ADD CONSTRAINT "DiscussionComment_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
ALTER TABLE "DiscussionComment" ADD CONSTRAINT "DiscussionComment_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
EXCEPTION WHEN duplicate_object THEN NULL; END $$;

View File

@ -6,46 +6,36 @@
-- Missing Foreign Keys -- Missing Foreign Keys
-- ===================================================== -- =====================================================
-- RoundTemplate -> Program -- RoundTemplate → Program
DO $$ BEGIN ALTER TABLE "RoundTemplate" ADD CONSTRAINT "RoundTemplate_programId_fkey"
ALTER TABLE "RoundTemplate" ADD CONSTRAINT "RoundTemplate_programId_fkey"
FOREIGN KEY ("programId") REFERENCES "Program"("id") ON DELETE CASCADE ON UPDATE CASCADE; FOREIGN KEY ("programId") REFERENCES "Program"("id") ON DELETE CASCADE ON UPDATE CASCADE;
EXCEPTION WHEN duplicate_object THEN NULL; END $$;
-- RoundTemplate -> User (creator) -- RoundTemplate → User (creator)
DO $$ BEGIN ALTER TABLE "RoundTemplate" ADD CONSTRAINT "RoundTemplate_createdBy_fkey"
ALTER TABLE "RoundTemplate" ADD CONSTRAINT "RoundTemplate_createdBy_fkey"
FOREIGN KEY ("createdBy") REFERENCES "User"("id") ON DELETE RESTRICT ON UPDATE CASCADE; FOREIGN KEY ("createdBy") REFERENCES "User"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
EXCEPTION WHEN duplicate_object THEN NULL; END $$;
-- Message -> Round -- Message → Round
DO $$ BEGIN ALTER TABLE "Message" ADD CONSTRAINT "Message_roundId_fkey"
ALTER TABLE "Message" ADD CONSTRAINT "Message_roundId_fkey"
FOREIGN KEY ("roundId") REFERENCES "Round"("id") ON DELETE SET NULL ON UPDATE CASCADE; FOREIGN KEY ("roundId") REFERENCES "Round"("id") ON DELETE SET NULL ON UPDATE CASCADE;
EXCEPTION WHEN duplicate_object THEN NULL; END $$;
-- EvaluationDiscussion -> Round -- EvaluationDiscussion → Round
DO $$ BEGIN ALTER TABLE "EvaluationDiscussion" ADD CONSTRAINT "EvaluationDiscussion_roundId_fkey"
ALTER TABLE "EvaluationDiscussion" ADD CONSTRAINT "EvaluationDiscussion_roundId_fkey"
FOREIGN KEY ("roundId") REFERENCES "Round"("id") ON DELETE CASCADE ON UPDATE CASCADE; FOREIGN KEY ("roundId") REFERENCES "Round"("id") ON DELETE CASCADE ON UPDATE CASCADE;
EXCEPTION WHEN duplicate_object THEN NULL; END $$;
-- ProjectFile -> ProjectFile (self-relation for file versioning) -- ProjectFile → ProjectFile (self-relation for file versioning)
DO $$ BEGIN ALTER TABLE "ProjectFile" ADD CONSTRAINT "ProjectFile_replacedById_fkey"
ALTER TABLE "ProjectFile" ADD CONSTRAINT "ProjectFile_replacedById_fkey"
FOREIGN KEY ("replacedById") REFERENCES "ProjectFile"("id") ON DELETE SET NULL ON UPDATE CASCADE; FOREIGN KEY ("replacedById") REFERENCES "ProjectFile"("id") ON DELETE SET NULL ON UPDATE CASCADE;
EXCEPTION WHEN duplicate_object THEN NULL; END $$;
-- ===================================================== -- =====================================================
-- Missing Indexes -- Missing Indexes
-- ===================================================== -- =====================================================
CREATE INDEX IF NOT EXISTS "RoundTemplate_roundType_idx" ON "RoundTemplate"("roundType"); CREATE INDEX "RoundTemplate_roundType_idx" ON "RoundTemplate"("roundType");
CREATE INDEX IF NOT EXISTS "MentorNote_authorId_idx" ON "MentorNote"("authorId"); CREATE INDEX "MentorNote_authorId_idx" ON "MentorNote"("authorId");
CREATE INDEX IF NOT EXISTS "MentorMilestoneCompletion_completedById_idx" ON "MentorMilestoneCompletion"("completedById"); CREATE INDEX "MentorMilestoneCompletion_completedById_idx" ON "MentorMilestoneCompletion"("completedById");
CREATE INDEX IF NOT EXISTS "Webhook_createdById_idx" ON "Webhook"("createdById"); CREATE INDEX "Webhook_createdById_idx" ON "Webhook"("createdById");
CREATE INDEX IF NOT EXISTS "WebhookDelivery_event_idx" ON "WebhookDelivery"("event"); CREATE INDEX "WebhookDelivery_event_idx" ON "WebhookDelivery"("event");
CREATE INDEX IF NOT EXISTS "Message_roundId_idx" ON "Message"("roundId"); CREATE INDEX "Message_roundId_idx" ON "Message"("roundId");
CREATE INDEX IF NOT EXISTS "EvaluationDiscussion_closedById_idx" ON "EvaluationDiscussion"("closedById"); CREATE INDEX "EvaluationDiscussion_closedById_idx" ON "EvaluationDiscussion"("closedById");
CREATE INDEX IF NOT EXISTS "DiscussionComment_discussionId_idx" ON "DiscussionComment"("discussionId"); CREATE INDEX "DiscussionComment_discussionId_idx" ON "DiscussionComment"("discussionId");
CREATE INDEX IF NOT EXISTS "DiscussionComment_userId_idx" ON "DiscussionComment"("userId"); CREATE INDEX "DiscussionComment_userId_idx" ON "DiscussionComment"("userId");

View File

@ -3,15 +3,11 @@
-- Add SET NULL on ProjectFile.roundId so deleting Round nullifies the reference -- Add SET NULL on ProjectFile.roundId so deleting Round nullifies the reference
-- AlterTable: Evaluation.formId -> onDelete CASCADE -- AlterTable: Evaluation.formId -> onDelete CASCADE
ALTER TABLE "Evaluation" DROP CONSTRAINT IF EXISTS "Evaluation_formId_fkey"; ALTER TABLE "Evaluation" DROP CONSTRAINT "Evaluation_formId_fkey";
DO $$ BEGIN ALTER TABLE "Evaluation" ADD CONSTRAINT "Evaluation_formId_fkey"
ALTER TABLE "Evaluation" ADD CONSTRAINT "Evaluation_formId_fkey"
FOREIGN KEY ("formId") REFERENCES "EvaluationForm"("id") ON DELETE CASCADE ON UPDATE CASCADE; FOREIGN KEY ("formId") REFERENCES "EvaluationForm"("id") ON DELETE CASCADE ON UPDATE CASCADE;
EXCEPTION WHEN duplicate_object THEN NULL; END $$;
-- AlterTable: ProjectFile.roundId -> onDelete SET NULL -- AlterTable: ProjectFile.roundId -> onDelete SET NULL
ALTER TABLE "ProjectFile" DROP CONSTRAINT IF EXISTS "ProjectFile_roundId_fkey"; ALTER TABLE "ProjectFile" DROP CONSTRAINT "ProjectFile_roundId_fkey";
DO $$ BEGIN ALTER TABLE "ProjectFile" ADD CONSTRAINT "ProjectFile_roundId_fkey"
ALTER TABLE "ProjectFile" ADD CONSTRAINT "ProjectFile_roundId_fkey"
FOREIGN KEY ("roundId") REFERENCES "Round"("id") ON DELETE SET NULL ON UPDATE CASCADE; FOREIGN KEY ("roundId") REFERENCES "Round"("id") ON DELETE SET NULL ON UPDATE CASCADE;
EXCEPTION WHEN duplicate_object THEN NULL; END $$;

View File

@ -1,5 +1,5 @@
-- CreateTable -- CreateTable
CREATE TABLE IF NOT EXISTS "FileRequirement" ( CREATE TABLE "FileRequirement" (
"id" TEXT NOT NULL, "id" TEXT NOT NULL,
"roundId" TEXT NOT NULL, "roundId" TEXT NOT NULL,
"name" TEXT NOT NULL, "name" TEXT NOT NULL,
@ -15,22 +15,16 @@ CREATE TABLE IF NOT EXISTS "FileRequirement" (
); );
-- CreateIndex -- CreateIndex
CREATE INDEX IF NOT EXISTS "FileRequirement_roundId_idx" ON "FileRequirement"("roundId"); CREATE INDEX "FileRequirement_roundId_idx" ON "FileRequirement"("roundId");
-- AddForeignKey -- AddForeignKey
DO $$ BEGIN ALTER TABLE "FileRequirement" ADD CONSTRAINT "FileRequirement_roundId_fkey" FOREIGN KEY ("roundId") REFERENCES "Round"("id") ON DELETE CASCADE ON UPDATE CASCADE;
ALTER TABLE "FileRequirement" ADD CONSTRAINT "FileRequirement_roundId_fkey" FOREIGN KEY ("roundId") REFERENCES "Round"("id") ON DELETE CASCADE ON UPDATE CASCADE;
EXCEPTION WHEN duplicate_object THEN NULL; END $$;
-- AlterTable: add requirementId to ProjectFile -- AlterTable: add requirementId to ProjectFile
DO $$ BEGIN ALTER TABLE "ProjectFile" ADD COLUMN "requirementId" TEXT;
ALTER TABLE "ProjectFile" ADD COLUMN "requirementId" TEXT;
EXCEPTION WHEN duplicate_column THEN NULL; END $$;
-- CreateIndex -- CreateIndex
CREATE INDEX IF NOT EXISTS "ProjectFile_requirementId_idx" ON "ProjectFile"("requirementId"); CREATE INDEX "ProjectFile_requirementId_idx" ON "ProjectFile"("requirementId");
-- AddForeignKey -- AddForeignKey
DO $$ BEGIN ALTER TABLE "ProjectFile" ADD CONSTRAINT "ProjectFile_requirementId_fkey" FOREIGN KEY ("requirementId") REFERENCES "FileRequirement"("id") ON DELETE SET NULL ON UPDATE CASCADE;
ALTER TABLE "ProjectFile" ADD CONSTRAINT "ProjectFile_requirementId_fkey" FOREIGN KEY ("requirementId") REFERENCES "FileRequirement"("id") ON DELETE SET NULL ON UPDATE CASCADE;
EXCEPTION WHEN duplicate_object THEN NULL; END $$;

View File

@ -1,2 +1,2 @@
-- CreateIndex -- CreateIndex
CREATE INDEX IF NOT EXISTS "AwardVote_awardId_userId_idx" ON "AwardVote"("awardId", "userId"); CREATE INDEX "AwardVote_awardId_userId_idx" ON "AwardVote"("awardId", "userId");

View File

@ -1,36 +1,28 @@
-- Simplify RoutingMode enum: remove POST_MAIN, rename PARALLEL -> SHARED -- Simplify RoutingMode enum: remove POST_MAIN, rename PARALLEL SHARED
-- Drop RoutingRule table (routing is now handled via award assignment) -- Drop RoutingRule table (routing is now handled via award assignment)
-- 1. Update existing PARALLEL values to SHARED, POST_MAIN to SHARED -- 1. Update existing PARALLEL values to SHARED, POST_MAIN to SHARED
-- (safe to run even if no rows match)
UPDATE "Track" SET "routingMode" = 'PARALLEL' WHERE "routingMode" = 'POST_MAIN'; UPDATE "Track" SET "routingMode" = 'PARALLEL' WHERE "routingMode" = 'POST_MAIN';
-- 2. Rename PARALLEL -> SHARED in the enum (only if PARALLEL still exists) -- 2. Rename PARALLEL → SHARED in the enum
DO $$ BEGIN ALTER TYPE "RoutingMode" RENAME VALUE 'PARALLEL' TO 'SHARED';
ALTER TYPE "RoutingMode" RENAME VALUE 'PARALLEL' TO 'SHARED';
EXCEPTION WHEN invalid_parameter_value THEN NULL; WHEN others THEN NULL; END $$;
-- 3. Remove POST_MAIN from the enum -- 3. Remove POST_MAIN from the enum
-- PostgreSQL doesn't support DROP VALUE directly, so we recreate the enum -- PostgreSQL doesn't support DROP VALUE directly, so we recreate the enum
-- Since we already converted POST_MAIN values to PARALLEL (now SHARED), this is safe -- Since we already converted POST_MAIN values to PARALLEL (now SHARED), this is safe
-- Only recreate if the old enum still has POST_MAIN (i.e., hasn't been done yet) -- Create new enum without POST_MAIN
DO $$ BEGIN -- Actually, since we already renamed PARALLEL to SHARED and converted POST_MAIN rows,
IF EXISTS ( -- we just need to remove the POST_MAIN value. PostgreSQL 13+ doesn't support dropping
SELECT 1 FROM pg_enum -- enum values natively, but since all rows are already migrated, we can:
WHERE enumlabel = 'POST_MAIN' CREATE TYPE "RoutingMode_new" AS ENUM ('SHARED', 'EXCLUSIVE');
AND enumtypid = (SELECT oid FROM pg_type WHERE typname = 'RoutingMode')
) THEN
CREATE TYPE "RoutingMode_new" AS ENUM ('SHARED', 'EXCLUSIVE');
ALTER TABLE "Track" ALTER TABLE "Track"
ALTER COLUMN "routingMode" TYPE "RoutingMode_new" ALTER COLUMN "routingMode" TYPE "RoutingMode_new"
USING ("routingMode"::text::"RoutingMode_new"); USING ("routingMode"::text::"RoutingMode_new");
DROP TYPE "RoutingMode"; DROP TYPE "RoutingMode";
ALTER TYPE "RoutingMode_new" RENAME TO "RoutingMode"; ALTER TYPE "RoutingMode_new" RENAME TO "RoutingMode";
END IF;
END $$;
-- 4. Drop the RoutingRule table (no longer needed) -- 4. Drop the RoutingRule table (no longer needed)
DROP TABLE IF EXISTS "RoutingRule"; DROP TABLE IF EXISTS "RoutingRule";

View File

@ -1,36 +1,36 @@
-- ============================================================================= -- =============================================================================
-- Phase 0+1: Add Competition/Round Architecture (additive -- no breaking changes) -- Phase 0+1: Add Competition/Round Architecture (additive no breaking changes)
-- ============================================================================= -- =============================================================================
-- New enums, new tables, new optional columns on existing tables. -- New enums, new tables, new optional columns on existing tables.
-- Old Pipeline/Track/Stage tables are untouched. -- Old Pipeline/Track/Stage tables are untouched.
-- --- New Enum Types --- -- ─── New Enum Types ──────────────────────────────────────────────────────────
DO $$ BEGIN CREATE TYPE "CompetitionStatus" AS ENUM ('DRAFT', 'ACTIVE', 'CLOSED', 'ARCHIVED'); EXCEPTION WHEN duplicate_object THEN NULL; END $$; CREATE TYPE "CompetitionStatus" AS ENUM ('DRAFT', 'ACTIVE', 'CLOSED', 'ARCHIVED');
DO $$ BEGIN CREATE TYPE "RoundType" AS ENUM ('INTAKE', 'FILTERING', 'EVALUATION', 'SUBMISSION', 'MENTORING', 'LIVE_FINAL', 'DELIBERATION'); EXCEPTION WHEN duplicate_object THEN NULL; END $$; CREATE TYPE "RoundType" AS ENUM ('INTAKE', 'FILTERING', 'EVALUATION', 'SUBMISSION', 'MENTORING', 'LIVE_FINAL', 'DELIBERATION');
DO $$ BEGIN CREATE TYPE "RoundStatus" AS ENUM ('ROUND_DRAFT', 'ROUND_ACTIVE', 'ROUND_CLOSED', 'ROUND_ARCHIVED'); EXCEPTION WHEN duplicate_object THEN NULL; END $$; CREATE TYPE "RoundStatus" AS ENUM ('ROUND_DRAFT', 'ROUND_ACTIVE', 'ROUND_CLOSED', 'ROUND_ARCHIVED');
DO $$ BEGIN CREATE TYPE "ProjectRoundStateValue" AS ENUM ('PENDING', 'IN_PROGRESS', 'PASSED', 'REJECTED', 'COMPLETED', 'WITHDRAWN'); EXCEPTION WHEN duplicate_object THEN NULL; END $$; CREATE TYPE "ProjectRoundStateValue" AS ENUM ('PENDING', 'IN_PROGRESS', 'PASSED', 'REJECTED', 'COMPLETED', 'WITHDRAWN');
DO $$ BEGIN CREATE TYPE "AdvancementRuleType" AS ENUM ('AUTO_ADVANCE', 'SCORE_THRESHOLD', 'TOP_N', 'ADMIN_SELECTION', 'AI_RECOMMENDED'); EXCEPTION WHEN duplicate_object THEN NULL; END $$; CREATE TYPE "AdvancementRuleType" AS ENUM ('AUTO_ADVANCE', 'SCORE_THRESHOLD', 'TOP_N', 'ADMIN_SELECTION', 'AI_RECOMMENDED');
DO $$ BEGIN CREATE TYPE "CapMode" AS ENUM ('HARD', 'SOFT', 'NONE'); EXCEPTION WHEN duplicate_object THEN NULL; END $$; CREATE TYPE "CapMode" AS ENUM ('HARD', 'SOFT', 'NONE');
DO $$ BEGIN CREATE TYPE "DeadlinePolicy" AS ENUM ('HARD_DEADLINE', 'FLAG', 'GRACE'); EXCEPTION WHEN duplicate_object THEN NULL; END $$; CREATE TYPE "DeadlinePolicy" AS ENUM ('HARD_DEADLINE', 'FLAG', 'GRACE');
DO $$ BEGIN CREATE TYPE "JuryGroupMemberRole" AS ENUM ('CHAIR', 'MEMBER', 'OBSERVER'); EXCEPTION WHEN duplicate_object THEN NULL; END $$; CREATE TYPE "JuryGroupMemberRole" AS ENUM ('CHAIR', 'MEMBER', 'OBSERVER');
DO $$ BEGIN CREATE TYPE "AssignmentIntentSource" AS ENUM ('INVITE', 'ADMIN', 'SYSTEM'); EXCEPTION WHEN duplicate_object THEN NULL; END $$; CREATE TYPE "AssignmentIntentSource" AS ENUM ('INVITE', 'ADMIN', 'SYSTEM');
DO $$ BEGIN CREATE TYPE "AssignmentIntentStatus" AS ENUM ('INTENT_PENDING', 'HONORED', 'OVERRIDDEN', 'EXPIRED', 'CANCELLED'); EXCEPTION WHEN duplicate_object THEN NULL; END $$; CREATE TYPE "AssignmentIntentStatus" AS ENUM ('INTENT_PENDING', 'HONORED', 'OVERRIDDEN', 'EXPIRED', 'CANCELLED');
DO $$ BEGIN CREATE TYPE "MentorMessageRole" AS ENUM ('MENTOR_ROLE', 'APPLICANT_ROLE', 'ADMIN_ROLE'); EXCEPTION WHEN duplicate_object THEN NULL; END $$; CREATE TYPE "MentorMessageRole" AS ENUM ('MENTOR_ROLE', 'APPLICANT_ROLE', 'ADMIN_ROLE');
DO $$ BEGIN CREATE TYPE "SubmissionPromotionSource" AS ENUM ('MENTOR_FILE', 'ADMIN_REPLACEMENT'); EXCEPTION WHEN duplicate_object THEN NULL; END $$; CREATE TYPE "SubmissionPromotionSource" AS ENUM ('MENTOR_FILE', 'ADMIN_REPLACEMENT');
DO $$ BEGIN CREATE TYPE "DeliberationMode" AS ENUM ('SINGLE_WINNER_VOTE', 'FULL_RANKING'); EXCEPTION WHEN duplicate_object THEN NULL; END $$; CREATE TYPE "DeliberationMode" AS ENUM ('SINGLE_WINNER_VOTE', 'FULL_RANKING');
DO $$ BEGIN CREATE TYPE "DeliberationStatus" AS ENUM ('DELIB_OPEN', 'VOTING', 'TALLYING', 'RUNOFF', 'DELIB_LOCKED'); EXCEPTION WHEN duplicate_object THEN NULL; END $$; CREATE TYPE "DeliberationStatus" AS ENUM ('DELIB_OPEN', 'VOTING', 'TALLYING', 'RUNOFF', 'DELIB_LOCKED');
DO $$ BEGIN CREATE TYPE "TieBreakMethod" AS ENUM ('TIE_RUNOFF', 'TIE_ADMIN_DECIDES', 'SCORE_FALLBACK'); EXCEPTION WHEN duplicate_object THEN NULL; END $$; CREATE TYPE "TieBreakMethod" AS ENUM ('TIE_RUNOFF', 'TIE_ADMIN_DECIDES', 'SCORE_FALLBACK');
DO $$ BEGIN CREATE TYPE "DeliberationParticipantStatus" AS ENUM ('REQUIRED', 'ABSENT_EXCUSED', 'REPLACED', 'REPLACEMENT_ACTIVE'); EXCEPTION WHEN duplicate_object THEN NULL; END $$; CREATE TYPE "DeliberationParticipantStatus" AS ENUM ('REQUIRED', 'ABSENT_EXCUSED', 'REPLACED', 'REPLACEMENT_ACTIVE');
DO $$ BEGIN CREATE TYPE "AwardEligibilityMode" AS ENUM ('SEPARATE_POOL', 'STAY_IN_MAIN'); EXCEPTION WHEN duplicate_object THEN NULL; END $$; CREATE TYPE "AwardEligibilityMode" AS ENUM ('SEPARATE_POOL', 'STAY_IN_MAIN');
-- Add FEATURE_FLAGS to SettingCategory enum -- Add FEATURE_FLAGS to SettingCategory enum
DO $$ BEGIN ALTER TYPE "SettingCategory" ADD VALUE 'FEATURE_FLAGS'; EXCEPTION WHEN duplicate_object THEN NULL; END $$; ALTER TYPE "SettingCategory" ADD VALUE 'FEATURE_FLAGS';
-- --- New Tables --- -- ─── New Tables ──────────────────────────────────────────────────────────────
-- Competition (replaces Pipeline) -- Competition (replaces Pipeline)
CREATE TABLE IF NOT EXISTS "Competition" ( CREATE TABLE "Competition" (
"id" TEXT NOT NULL, "id" TEXT NOT NULL,
"programId" TEXT NOT NULL, "programId" TEXT NOT NULL,
"name" TEXT NOT NULL, "name" TEXT NOT NULL,
@ -49,7 +49,7 @@ CREATE TABLE IF NOT EXISTS "Competition" (
); );
-- Round (replaces Stage) -- Round (replaces Stage)
CREATE TABLE IF NOT EXISTS "Round" ( CREATE TABLE "Round" (
"id" TEXT NOT NULL, "id" TEXT NOT NULL,
"competitionId" TEXT NOT NULL, "competitionId" TEXT NOT NULL,
"name" TEXT NOT NULL, "name" TEXT NOT NULL,
@ -70,7 +70,7 @@ CREATE TABLE IF NOT EXISTS "Round" (
); );
-- ProjectRoundState -- ProjectRoundState
CREATE TABLE IF NOT EXISTS "ProjectRoundState" ( CREATE TABLE "ProjectRoundState" (
"id" TEXT NOT NULL, "id" TEXT NOT NULL,
"projectId" TEXT NOT NULL, "projectId" TEXT NOT NULL,
"roundId" TEXT NOT NULL, "roundId" TEXT NOT NULL,
@ -85,7 +85,7 @@ CREATE TABLE IF NOT EXISTS "ProjectRoundState" (
); );
-- AdvancementRule -- AdvancementRule
CREATE TABLE IF NOT EXISTS "AdvancementRule" ( CREATE TABLE "AdvancementRule" (
"id" TEXT NOT NULL, "id" TEXT NOT NULL,
"roundId" TEXT NOT NULL, "roundId" TEXT NOT NULL,
"targetRoundId" TEXT, "targetRoundId" TEXT,
@ -99,7 +99,7 @@ CREATE TABLE IF NOT EXISTS "AdvancementRule" (
); );
-- JuryGroup -- JuryGroup
CREATE TABLE IF NOT EXISTS "JuryGroup" ( CREATE TABLE "JuryGroup" (
"id" TEXT NOT NULL, "id" TEXT NOT NULL,
"competitionId" TEXT NOT NULL, "competitionId" TEXT NOT NULL,
"name" TEXT NOT NULL, "name" TEXT NOT NULL,
@ -120,7 +120,7 @@ CREATE TABLE IF NOT EXISTS "JuryGroup" (
); );
-- JuryGroupMember -- JuryGroupMember
CREATE TABLE IF NOT EXISTS "JuryGroupMember" ( CREATE TABLE "JuryGroupMember" (
"id" TEXT NOT NULL, "id" TEXT NOT NULL,
"juryGroupId" TEXT NOT NULL, "juryGroupId" TEXT NOT NULL,
"userId" TEXT NOT NULL, "userId" TEXT NOT NULL,
@ -138,7 +138,7 @@ CREATE TABLE IF NOT EXISTS "JuryGroupMember" (
); );
-- SubmissionWindow -- SubmissionWindow
CREATE TABLE IF NOT EXISTS "SubmissionWindow" ( CREATE TABLE "SubmissionWindow" (
"id" TEXT NOT NULL, "id" TEXT NOT NULL,
"competitionId" TEXT NOT NULL, "competitionId" TEXT NOT NULL,
"name" TEXT NOT NULL, "name" TEXT NOT NULL,
@ -158,7 +158,7 @@ CREATE TABLE IF NOT EXISTS "SubmissionWindow" (
); );
-- SubmissionFileRequirement -- SubmissionFileRequirement
CREATE TABLE IF NOT EXISTS "SubmissionFileRequirement" ( CREATE TABLE "SubmissionFileRequirement" (
"id" TEXT NOT NULL, "id" TEXT NOT NULL,
"submissionWindowId" TEXT NOT NULL, "submissionWindowId" TEXT NOT NULL,
"label" TEXT NOT NULL, "label" TEXT NOT NULL,
@ -175,7 +175,7 @@ CREATE TABLE IF NOT EXISTS "SubmissionFileRequirement" (
); );
-- RoundSubmissionVisibility -- RoundSubmissionVisibility
CREATE TABLE IF NOT EXISTS "RoundSubmissionVisibility" ( CREATE TABLE "RoundSubmissionVisibility" (
"id" TEXT NOT NULL, "id" TEXT NOT NULL,
"roundId" TEXT NOT NULL, "roundId" TEXT NOT NULL,
"submissionWindowId" TEXT NOT NULL, "submissionWindowId" TEXT NOT NULL,
@ -186,7 +186,7 @@ CREATE TABLE IF NOT EXISTS "RoundSubmissionVisibility" (
); );
-- AssignmentIntent -- AssignmentIntent
CREATE TABLE IF NOT EXISTS "AssignmentIntent" ( CREATE TABLE "AssignmentIntent" (
"id" TEXT NOT NULL, "id" TEXT NOT NULL,
"juryGroupMemberId" TEXT NOT NULL, "juryGroupMemberId" TEXT NOT NULL,
"roundId" TEXT NOT NULL, "roundId" TEXT NOT NULL,
@ -200,7 +200,7 @@ CREATE TABLE IF NOT EXISTS "AssignmentIntent" (
); );
-- AssignmentException -- AssignmentException
CREATE TABLE IF NOT EXISTS "AssignmentException" ( CREATE TABLE "AssignmentException" (
"id" TEXT NOT NULL, "id" TEXT NOT NULL,
"assignmentId" TEXT NOT NULL, "assignmentId" TEXT NOT NULL,
"reason" TEXT NOT NULL, "reason" TEXT NOT NULL,
@ -212,7 +212,7 @@ CREATE TABLE IF NOT EXISTS "AssignmentException" (
); );
-- MentorFile -- MentorFile
CREATE TABLE IF NOT EXISTS "MentorFile" ( CREATE TABLE "MentorFile" (
"id" TEXT NOT NULL, "id" TEXT NOT NULL,
"mentorAssignmentId" TEXT NOT NULL, "mentorAssignmentId" TEXT NOT NULL,
"uploadedByUserId" TEXT NOT NULL, "uploadedByUserId" TEXT NOT NULL,
@ -232,7 +232,7 @@ CREATE TABLE IF NOT EXISTS "MentorFile" (
); );
-- MentorFileComment -- MentorFileComment
CREATE TABLE IF NOT EXISTS "MentorFileComment" ( CREATE TABLE "MentorFileComment" (
"id" TEXT NOT NULL, "id" TEXT NOT NULL,
"mentorFileId" TEXT NOT NULL, "mentorFileId" TEXT NOT NULL,
"authorId" TEXT NOT NULL, "authorId" TEXT NOT NULL,
@ -245,7 +245,7 @@ CREATE TABLE IF NOT EXISTS "MentorFileComment" (
); );
-- SubmissionPromotionEvent -- SubmissionPromotionEvent
CREATE TABLE IF NOT EXISTS "SubmissionPromotionEvent" ( CREATE TABLE "SubmissionPromotionEvent" (
"id" TEXT NOT NULL, "id" TEXT NOT NULL,
"projectId" TEXT NOT NULL, "projectId" TEXT NOT NULL,
"roundId" TEXT NOT NULL, "roundId" TEXT NOT NULL,
@ -259,7 +259,7 @@ CREATE TABLE IF NOT EXISTS "SubmissionPromotionEvent" (
); );
-- DeliberationSession -- DeliberationSession
CREATE TABLE IF NOT EXISTS "DeliberationSession" ( CREATE TABLE "DeliberationSession" (
"id" TEXT NOT NULL, "id" TEXT NOT NULL,
"competitionId" TEXT NOT NULL, "competitionId" TEXT NOT NULL,
"roundId" TEXT NOT NULL, "roundId" TEXT NOT NULL,
@ -277,7 +277,7 @@ CREATE TABLE IF NOT EXISTS "DeliberationSession" (
); );
-- DeliberationVote -- DeliberationVote
CREATE TABLE IF NOT EXISTS "DeliberationVote" ( CREATE TABLE "DeliberationVote" (
"id" TEXT NOT NULL, "id" TEXT NOT NULL,
"sessionId" TEXT NOT NULL, "sessionId" TEXT NOT NULL,
"juryMemberId" TEXT NOT NULL, "juryMemberId" TEXT NOT NULL,
@ -291,7 +291,7 @@ CREATE TABLE IF NOT EXISTS "DeliberationVote" (
); );
-- DeliberationResult -- DeliberationResult
CREATE TABLE IF NOT EXISTS "DeliberationResult" ( CREATE TABLE "DeliberationResult" (
"id" TEXT NOT NULL, "id" TEXT NOT NULL,
"sessionId" TEXT NOT NULL, "sessionId" TEXT NOT NULL,
"projectId" TEXT NOT NULL, "projectId" TEXT NOT NULL,
@ -304,7 +304,7 @@ CREATE TABLE IF NOT EXISTS "DeliberationResult" (
); );
-- DeliberationParticipant -- DeliberationParticipant
CREATE TABLE IF NOT EXISTS "DeliberationParticipant" ( CREATE TABLE "DeliberationParticipant" (
"id" TEXT NOT NULL, "id" TEXT NOT NULL,
"sessionId" TEXT NOT NULL, "sessionId" TEXT NOT NULL,
"userId" TEXT NOT NULL, "userId" TEXT NOT NULL,
@ -315,7 +315,7 @@ CREATE TABLE IF NOT EXISTS "DeliberationParticipant" (
); );
-- ResultLock -- ResultLock
CREATE TABLE IF NOT EXISTS "ResultLock" ( CREATE TABLE "ResultLock" (
"id" TEXT NOT NULL, "id" TEXT NOT NULL,
"competitionId" TEXT NOT NULL, "competitionId" TEXT NOT NULL,
"roundId" TEXT NOT NULL, "roundId" TEXT NOT NULL,
@ -328,7 +328,7 @@ CREATE TABLE IF NOT EXISTS "ResultLock" (
); );
-- ResultUnlockEvent -- ResultUnlockEvent
CREATE TABLE IF NOT EXISTS "ResultUnlockEvent" ( CREATE TABLE "ResultUnlockEvent" (
"id" TEXT NOT NULL, "id" TEXT NOT NULL,
"resultLockId" TEXT NOT NULL, "resultLockId" TEXT NOT NULL,
"unlockedById" TEXT NOT NULL, "unlockedById" TEXT NOT NULL,
@ -338,365 +338,235 @@ CREATE TABLE IF NOT EXISTS "ResultUnlockEvent" (
CONSTRAINT "ResultUnlockEvent_pkey" PRIMARY KEY ("id") CONSTRAINT "ResultUnlockEvent_pkey" PRIMARY KEY ("id")
); );
-- --- Add Columns to Existing Tables --- -- ─── Add Columns to Existing Tables ──────────────────────────────────────────
-- Assignment: add juryGroupId -- Assignment: add juryGroupId
DO $$ BEGIN ALTER TABLE "Assignment" ADD COLUMN "juryGroupId" TEXT;
ALTER TABLE "Assignment" ADD COLUMN "juryGroupId" TEXT;
EXCEPTION WHEN duplicate_column THEN NULL; END $$;
-- SpecialAward: add competition/round architecture fields -- SpecialAward: add competition/round architecture fields
DO $$ BEGIN ALTER TABLE "SpecialAward" ADD COLUMN "competitionId" TEXT;
ALTER TABLE "SpecialAward" ADD COLUMN "competitionId" TEXT; ALTER TABLE "SpecialAward" ADD COLUMN "evaluationRoundId" TEXT;
EXCEPTION WHEN duplicate_column THEN NULL; END $$; ALTER TABLE "SpecialAward" ADD COLUMN "juryGroupId" TEXT;
DO $$ BEGIN ALTER TABLE "SpecialAward" ADD COLUMN "eligibilityMode" "AwardEligibilityMode" NOT NULL DEFAULT 'STAY_IN_MAIN';
ALTER TABLE "SpecialAward" ADD COLUMN "evaluationRoundId" TEXT; ALTER TABLE "SpecialAward" ADD COLUMN "decisionMode" TEXT;
EXCEPTION WHEN duplicate_column THEN NULL; END $$;
DO $$ BEGIN
ALTER TABLE "SpecialAward" ADD COLUMN "juryGroupId" TEXT;
EXCEPTION WHEN duplicate_column THEN NULL; END $$;
DO $$ BEGIN
ALTER TABLE "SpecialAward" ADD COLUMN "eligibilityMode" "AwardEligibilityMode" NOT NULL DEFAULT 'STAY_IN_MAIN';
EXCEPTION WHEN duplicate_column THEN NULL; END $$;
DO $$ BEGIN
ALTER TABLE "SpecialAward" ADD COLUMN "decisionMode" TEXT;
EXCEPTION WHEN duplicate_column THEN NULL; END $$;
-- MentorAssignment: add workspace fields -- MentorAssignment: add workspace fields
DO $$ BEGIN ALTER TABLE "MentorAssignment" ADD COLUMN "workspaceEnabled" BOOLEAN NOT NULL DEFAULT false;
ALTER TABLE "MentorAssignment" ADD COLUMN "workspaceEnabled" BOOLEAN NOT NULL DEFAULT false; ALTER TABLE "MentorAssignment" ADD COLUMN "workspaceOpenAt" TIMESTAMP(3);
EXCEPTION WHEN duplicate_column THEN NULL; END $$; ALTER TABLE "MentorAssignment" ADD COLUMN "workspaceCloseAt" TIMESTAMP(3);
DO $$ BEGIN
ALTER TABLE "MentorAssignment" ADD COLUMN "workspaceOpenAt" TIMESTAMP(3);
EXCEPTION WHEN duplicate_column THEN NULL; END $$;
DO $$ BEGIN
ALTER TABLE "MentorAssignment" ADD COLUMN "workspaceCloseAt" TIMESTAMP(3);
EXCEPTION WHEN duplicate_column THEN NULL; END $$;
-- MentorMessage: add workspace fields -- MentorMessage: add workspace fields
DO $$ BEGIN ALTER TABLE "MentorMessage" ADD COLUMN "workspaceId" TEXT;
ALTER TABLE "MentorMessage" ADD COLUMN "workspaceId" TEXT; ALTER TABLE "MentorMessage" ADD COLUMN "senderRole" "MentorMessageRole";
EXCEPTION WHEN duplicate_column THEN NULL; END $$;
DO $$ BEGIN
ALTER TABLE "MentorMessage" ADD COLUMN "senderRole" "MentorMessageRole";
EXCEPTION WHEN duplicate_column THEN NULL; END $$;
-- ProjectFile: add submission window link -- ProjectFile: add submission window link
DO $$ BEGIN ALTER TABLE "ProjectFile" ADD COLUMN "submissionWindowId" TEXT;
ALTER TABLE "ProjectFile" ADD COLUMN "submissionWindowId" TEXT; ALTER TABLE "ProjectFile" ADD COLUMN "submissionFileRequirementId" TEXT;
EXCEPTION WHEN duplicate_column THEN NULL; END $$;
DO $$ BEGIN
ALTER TABLE "ProjectFile" ADD COLUMN "submissionFileRequirementId" TEXT;
EXCEPTION WHEN duplicate_column THEN NULL; END $$;
-- --- Unique Constraints --- -- ─── Unique Constraints ──────────────────────────────────────────────────────
CREATE UNIQUE INDEX IF NOT EXISTS "Competition_slug_key" ON "Competition"("slug"); CREATE UNIQUE INDEX "Competition_slug_key" ON "Competition"("slug");
CREATE UNIQUE INDEX IF NOT EXISTS "Round_competitionId_slug_key" ON "Round"("competitionId", "slug"); CREATE UNIQUE INDEX "Round_competitionId_slug_key" ON "Round"("competitionId", "slug");
CREATE UNIQUE INDEX IF NOT EXISTS "Round_competitionId_sortOrder_key" ON "Round"("competitionId", "sortOrder"); CREATE UNIQUE INDEX "Round_competitionId_sortOrder_key" ON "Round"("competitionId", "sortOrder");
CREATE UNIQUE INDEX IF NOT EXISTS "ProjectRoundState_projectId_roundId_key" ON "ProjectRoundState"("projectId", "roundId"); CREATE UNIQUE INDEX "ProjectRoundState_projectId_roundId_key" ON "ProjectRoundState"("projectId", "roundId");
CREATE UNIQUE INDEX IF NOT EXISTS "JuryGroup_competitionId_slug_key" ON "JuryGroup"("competitionId", "slug"); CREATE UNIQUE INDEX "JuryGroup_competitionId_slug_key" ON "JuryGroup"("competitionId", "slug");
CREATE UNIQUE INDEX IF NOT EXISTS "JuryGroupMember_juryGroupId_userId_key" ON "JuryGroupMember"("juryGroupId", "userId"); CREATE UNIQUE INDEX "JuryGroupMember_juryGroupId_userId_key" ON "JuryGroupMember"("juryGroupId", "userId");
CREATE UNIQUE INDEX IF NOT EXISTS "SubmissionWindow_competitionId_slug_key" ON "SubmissionWindow"("competitionId", "slug"); CREATE UNIQUE INDEX "SubmissionWindow_competitionId_slug_key" ON "SubmissionWindow"("competitionId", "slug");
CREATE UNIQUE INDEX IF NOT EXISTS "SubmissionWindow_competitionId_roundNumber_key" ON "SubmissionWindow"("competitionId", "roundNumber"); CREATE UNIQUE INDEX "SubmissionWindow_competitionId_roundNumber_key" ON "SubmissionWindow"("competitionId", "roundNumber");
CREATE UNIQUE INDEX IF NOT EXISTS "RoundSubmissionVisibility_roundId_submissionWindowId_key" ON "RoundSubmissionVisibility"("roundId", "submissionWindowId"); CREATE UNIQUE INDEX "RoundSubmissionVisibility_roundId_submissionWindowId_key" ON "RoundSubmissionVisibility"("roundId", "submissionWindowId");
CREATE UNIQUE INDEX IF NOT EXISTS "AssignmentIntent_juryGroupMemberId_roundId_projectId_key" ON "AssignmentIntent"("juryGroupMemberId", "roundId", "projectId"); CREATE UNIQUE INDEX "AssignmentIntent_juryGroupMemberId_roundId_projectId_key" ON "AssignmentIntent"("juryGroupMemberId", "roundId", "projectId");
CREATE UNIQUE INDEX IF NOT EXISTS "MentorFile_promotedToFileId_key" ON "MentorFile"("promotedToFileId"); CREATE UNIQUE INDEX "MentorFile_promotedToFileId_key" ON "MentorFile"("promotedToFileId");
CREATE UNIQUE INDEX IF NOT EXISTS "DeliberationVote_sessionId_juryMemberId_projectId_runoffRo_key" ON "DeliberationVote"("sessionId", "juryMemberId", "projectId", "runoffRound"); CREATE UNIQUE INDEX "DeliberationVote_sessionId_juryMemberId_projectId_runoffRo_key" ON "DeliberationVote"("sessionId", "juryMemberId", "projectId", "runoffRound");
CREATE UNIQUE INDEX IF NOT EXISTS "DeliberationResult_sessionId_projectId_key" ON "DeliberationResult"("sessionId", "projectId"); CREATE UNIQUE INDEX "DeliberationResult_sessionId_projectId_key" ON "DeliberationResult"("sessionId", "projectId");
CREATE UNIQUE INDEX IF NOT EXISTS "DeliberationParticipant_sessionId_userId_key" ON "DeliberationParticipant"("sessionId", "userId"); CREATE UNIQUE INDEX "DeliberationParticipant_sessionId_userId_key" ON "DeliberationParticipant"("sessionId", "userId");
CREATE UNIQUE INDEX IF NOT EXISTS "SubmissionFileRequirement_submissionWindowId_slug_key" ON "SubmissionFileRequirement"("submissionWindowId", "slug"); CREATE UNIQUE INDEX "SubmissionFileRequirement_submissionWindowId_slug_key" ON "SubmissionFileRequirement"("submissionWindowId", "slug");
CREATE UNIQUE INDEX IF NOT EXISTS "AdvancementRule_roundId_sortOrder_key" ON "AdvancementRule"("roundId", "sortOrder"); CREATE UNIQUE INDEX "AdvancementRule_roundId_sortOrder_key" ON "AdvancementRule"("roundId", "sortOrder");
-- --- Indexes --- -- ─── Indexes ─────────────────────────────────────────────────────────────────
-- Competition -- Competition
CREATE INDEX IF NOT EXISTS "Competition_programId_idx" ON "Competition"("programId"); CREATE INDEX "Competition_programId_idx" ON "Competition"("programId");
CREATE INDEX IF NOT EXISTS "Competition_status_idx" ON "Competition"("status"); CREATE INDEX "Competition_status_idx" ON "Competition"("status");
-- Round -- Round
CREATE INDEX IF NOT EXISTS "Round_competitionId_idx" ON "Round"("competitionId"); CREATE INDEX "Round_competitionId_idx" ON "Round"("competitionId");
CREATE INDEX IF NOT EXISTS "Round_roundType_idx" ON "Round"("roundType"); CREATE INDEX "Round_roundType_idx" ON "Round"("roundType");
CREATE INDEX IF NOT EXISTS "Round_status_idx" ON "Round"("status"); CREATE INDEX "Round_status_idx" ON "Round"("status");
-- ProjectRoundState -- ProjectRoundState
CREATE INDEX IF NOT EXISTS "ProjectRoundState_projectId_idx" ON "ProjectRoundState"("projectId"); CREATE INDEX "ProjectRoundState_projectId_idx" ON "ProjectRoundState"("projectId");
CREATE INDEX IF NOT EXISTS "ProjectRoundState_roundId_idx" ON "ProjectRoundState"("roundId"); CREATE INDEX "ProjectRoundState_roundId_idx" ON "ProjectRoundState"("roundId");
CREATE INDEX IF NOT EXISTS "ProjectRoundState_state_idx" ON "ProjectRoundState"("state"); CREATE INDEX "ProjectRoundState_state_idx" ON "ProjectRoundState"("state");
-- AdvancementRule -- AdvancementRule
CREATE INDEX IF NOT EXISTS "AdvancementRule_roundId_idx" ON "AdvancementRule"("roundId"); CREATE INDEX "AdvancementRule_roundId_idx" ON "AdvancementRule"("roundId");
-- JuryGroup -- JuryGroup
CREATE INDEX IF NOT EXISTS "JuryGroup_competitionId_idx" ON "JuryGroup"("competitionId"); CREATE INDEX "JuryGroup_competitionId_idx" ON "JuryGroup"("competitionId");
-- JuryGroupMember -- JuryGroupMember
CREATE INDEX IF NOT EXISTS "JuryGroupMember_juryGroupId_idx" ON "JuryGroupMember"("juryGroupId"); CREATE INDEX "JuryGroupMember_juryGroupId_idx" ON "JuryGroupMember"("juryGroupId");
CREATE INDEX IF NOT EXISTS "JuryGroupMember_userId_idx" ON "JuryGroupMember"("userId"); CREATE INDEX "JuryGroupMember_userId_idx" ON "JuryGroupMember"("userId");
-- SubmissionWindow -- SubmissionWindow
CREATE INDEX IF NOT EXISTS "SubmissionWindow_competitionId_idx" ON "SubmissionWindow"("competitionId"); CREATE INDEX "SubmissionWindow_competitionId_idx" ON "SubmissionWindow"("competitionId");
-- SubmissionFileRequirement -- SubmissionFileRequirement
CREATE INDEX IF NOT EXISTS "SubmissionFileRequirement_submissionWindowId_idx" ON "SubmissionFileRequirement"("submissionWindowId"); CREATE INDEX "SubmissionFileRequirement_submissionWindowId_idx" ON "SubmissionFileRequirement"("submissionWindowId");
-- RoundSubmissionVisibility -- RoundSubmissionVisibility
CREATE INDEX IF NOT EXISTS "RoundSubmissionVisibility_roundId_idx" ON "RoundSubmissionVisibility"("roundId"); CREATE INDEX "RoundSubmissionVisibility_roundId_idx" ON "RoundSubmissionVisibility"("roundId");
-- AssignmentIntent -- AssignmentIntent
CREATE INDEX IF NOT EXISTS "AssignmentIntent_roundId_idx" ON "AssignmentIntent"("roundId"); CREATE INDEX "AssignmentIntent_roundId_idx" ON "AssignmentIntent"("roundId");
CREATE INDEX IF NOT EXISTS "AssignmentIntent_projectId_idx" ON "AssignmentIntent"("projectId"); CREATE INDEX "AssignmentIntent_projectId_idx" ON "AssignmentIntent"("projectId");
CREATE INDEX IF NOT EXISTS "AssignmentIntent_status_idx" ON "AssignmentIntent"("status"); CREATE INDEX "AssignmentIntent_status_idx" ON "AssignmentIntent"("status");
-- AssignmentException -- AssignmentException
CREATE INDEX IF NOT EXISTS "AssignmentException_assignmentId_idx" ON "AssignmentException"("assignmentId"); CREATE INDEX "AssignmentException_assignmentId_idx" ON "AssignmentException"("assignmentId");
CREATE INDEX IF NOT EXISTS "AssignmentException_approvedById_idx" ON "AssignmentException"("approvedById"); CREATE INDEX "AssignmentException_approvedById_idx" ON "AssignmentException"("approvedById");
-- MentorFile -- MentorFile
CREATE INDEX IF NOT EXISTS "MentorFile_mentorAssignmentId_idx" ON "MentorFile"("mentorAssignmentId"); CREATE INDEX "MentorFile_mentorAssignmentId_idx" ON "MentorFile"("mentorAssignmentId");
CREATE INDEX IF NOT EXISTS "MentorFile_uploadedByUserId_idx" ON "MentorFile"("uploadedByUserId"); CREATE INDEX "MentorFile_uploadedByUserId_idx" ON "MentorFile"("uploadedByUserId");
-- MentorFileComment -- MentorFileComment
CREATE INDEX IF NOT EXISTS "MentorFileComment_mentorFileId_idx" ON "MentorFileComment"("mentorFileId"); CREATE INDEX "MentorFileComment_mentorFileId_idx" ON "MentorFileComment"("mentorFileId");
CREATE INDEX IF NOT EXISTS "MentorFileComment_authorId_idx" ON "MentorFileComment"("authorId"); CREATE INDEX "MentorFileComment_authorId_idx" ON "MentorFileComment"("authorId");
CREATE INDEX IF NOT EXISTS "MentorFileComment_parentCommentId_idx" ON "MentorFileComment"("parentCommentId"); CREATE INDEX "MentorFileComment_parentCommentId_idx" ON "MentorFileComment"("parentCommentId");
-- SubmissionPromotionEvent -- SubmissionPromotionEvent
CREATE INDEX IF NOT EXISTS "SubmissionPromotionEvent_projectId_idx" ON "SubmissionPromotionEvent"("projectId"); CREATE INDEX "SubmissionPromotionEvent_projectId_idx" ON "SubmissionPromotionEvent"("projectId");
CREATE INDEX IF NOT EXISTS "SubmissionPromotionEvent_roundId_idx" ON "SubmissionPromotionEvent"("roundId"); CREATE INDEX "SubmissionPromotionEvent_roundId_idx" ON "SubmissionPromotionEvent"("roundId");
CREATE INDEX IF NOT EXISTS "SubmissionPromotionEvent_sourceFileId_idx" ON "SubmissionPromotionEvent"("sourceFileId"); CREATE INDEX "SubmissionPromotionEvent_sourceFileId_idx" ON "SubmissionPromotionEvent"("sourceFileId");
-- DeliberationSession -- DeliberationSession
CREATE INDEX IF NOT EXISTS "DeliberationSession_competitionId_idx" ON "DeliberationSession"("competitionId"); CREATE INDEX "DeliberationSession_competitionId_idx" ON "DeliberationSession"("competitionId");
CREATE INDEX IF NOT EXISTS "DeliberationSession_roundId_idx" ON "DeliberationSession"("roundId"); CREATE INDEX "DeliberationSession_roundId_idx" ON "DeliberationSession"("roundId");
CREATE INDEX IF NOT EXISTS "DeliberationSession_status_idx" ON "DeliberationSession"("status"); CREATE INDEX "DeliberationSession_status_idx" ON "DeliberationSession"("status");
-- DeliberationVote -- DeliberationVote
CREATE INDEX IF NOT EXISTS "DeliberationVote_sessionId_idx" ON "DeliberationVote"("sessionId"); CREATE INDEX "DeliberationVote_sessionId_idx" ON "DeliberationVote"("sessionId");
CREATE INDEX IF NOT EXISTS "DeliberationVote_juryMemberId_idx" ON "DeliberationVote"("juryMemberId"); CREATE INDEX "DeliberationVote_juryMemberId_idx" ON "DeliberationVote"("juryMemberId");
CREATE INDEX IF NOT EXISTS "DeliberationVote_projectId_idx" ON "DeliberationVote"("projectId"); CREATE INDEX "DeliberationVote_projectId_idx" ON "DeliberationVote"("projectId");
-- DeliberationResult -- DeliberationResult
CREATE INDEX IF NOT EXISTS "DeliberationResult_sessionId_idx" ON "DeliberationResult"("sessionId"); CREATE INDEX "DeliberationResult_sessionId_idx" ON "DeliberationResult"("sessionId");
CREATE INDEX IF NOT EXISTS "DeliberationResult_projectId_idx" ON "DeliberationResult"("projectId"); CREATE INDEX "DeliberationResult_projectId_idx" ON "DeliberationResult"("projectId");
-- DeliberationParticipant -- DeliberationParticipant
CREATE INDEX IF NOT EXISTS "DeliberationParticipant_sessionId_idx" ON "DeliberationParticipant"("sessionId"); CREATE INDEX "DeliberationParticipant_sessionId_idx" ON "DeliberationParticipant"("sessionId");
CREATE INDEX IF NOT EXISTS "DeliberationParticipant_userId_idx" ON "DeliberationParticipant"("userId"); CREATE INDEX "DeliberationParticipant_userId_idx" ON "DeliberationParticipant"("userId");
-- ResultLock -- ResultLock
CREATE INDEX IF NOT EXISTS "ResultLock_competitionId_idx" ON "ResultLock"("competitionId"); CREATE INDEX "ResultLock_competitionId_idx" ON "ResultLock"("competitionId");
CREATE INDEX IF NOT EXISTS "ResultLock_roundId_idx" ON "ResultLock"("roundId"); CREATE INDEX "ResultLock_roundId_idx" ON "ResultLock"("roundId");
CREATE INDEX IF NOT EXISTS "ResultLock_category_idx" ON "ResultLock"("category"); CREATE INDEX "ResultLock_category_idx" ON "ResultLock"("category");
-- ResultUnlockEvent -- ResultUnlockEvent
CREATE INDEX IF NOT EXISTS "ResultUnlockEvent_resultLockId_idx" ON "ResultUnlockEvent"("resultLockId"); CREATE INDEX "ResultUnlockEvent_resultLockId_idx" ON "ResultUnlockEvent"("resultLockId");
CREATE INDEX IF NOT EXISTS "ResultUnlockEvent_unlockedById_idx" ON "ResultUnlockEvent"("unlockedById"); CREATE INDEX "ResultUnlockEvent_unlockedById_idx" ON "ResultUnlockEvent"("unlockedById");
-- Indexes on modified existing tables -- Indexes on modified existing tables
CREATE INDEX IF NOT EXISTS "Assignment_juryGroupId_idx" ON "Assignment"("juryGroupId"); CREATE INDEX "Assignment_juryGroupId_idx" ON "Assignment"("juryGroupId");
CREATE INDEX IF NOT EXISTS "SpecialAward_competitionId_idx" ON "SpecialAward"("competitionId"); CREATE INDEX "SpecialAward_competitionId_idx" ON "SpecialAward"("competitionId");
CREATE INDEX IF NOT EXISTS "SpecialAward_evaluationRoundId_idx" ON "SpecialAward"("evaluationRoundId"); CREATE INDEX "SpecialAward_evaluationRoundId_idx" ON "SpecialAward"("evaluationRoundId");
CREATE INDEX IF NOT EXISTS "MentorMessage_workspaceId_idx" ON "MentorMessage"("workspaceId"); CREATE INDEX "MentorMessage_workspaceId_idx" ON "MentorMessage"("workspaceId");
CREATE INDEX IF NOT EXISTS "ProjectFile_submissionWindowId_idx" ON "ProjectFile"("submissionWindowId"); CREATE INDEX "ProjectFile_submissionWindowId_idx" ON "ProjectFile"("submissionWindowId");
CREATE INDEX IF NOT EXISTS "ProjectFile_submissionFileRequirementId_idx" ON "ProjectFile"("submissionFileRequirementId"); CREATE INDEX "ProjectFile_submissionFileRequirementId_idx" ON "ProjectFile"("submissionFileRequirementId");
-- --- Foreign Keys --- -- ─── Foreign Keys ────────────────────────────────────────────────────────────
-- Competition -- Competition
DO $$ BEGIN ALTER TABLE "Competition" ADD CONSTRAINT "Competition_programId_fkey" FOREIGN KEY ("programId") REFERENCES "Program"("id") ON DELETE CASCADE ON UPDATE CASCADE;
ALTER TABLE "Competition" ADD CONSTRAINT "Competition_programId_fkey" FOREIGN KEY ("programId") REFERENCES "Program"("id") ON DELETE CASCADE ON UPDATE CASCADE;
EXCEPTION WHEN duplicate_object THEN NULL; END $$;
-- Round -- Round
DO $$ BEGIN ALTER TABLE "Round" ADD CONSTRAINT "Round_competitionId_fkey" FOREIGN KEY ("competitionId") REFERENCES "Competition"("id") ON DELETE CASCADE ON UPDATE CASCADE;
ALTER TABLE "Round" ADD CONSTRAINT "Round_competitionId_fkey" FOREIGN KEY ("competitionId") REFERENCES "Competition"("id") ON DELETE CASCADE ON UPDATE CASCADE; ALTER TABLE "Round" ADD CONSTRAINT "Round_juryGroupId_fkey" FOREIGN KEY ("juryGroupId") REFERENCES "JuryGroup"("id") ON DELETE SET NULL ON UPDATE CASCADE;
EXCEPTION WHEN duplicate_object THEN NULL; END $$; ALTER TABLE "Round" ADD CONSTRAINT "Round_submissionWindowId_fkey" FOREIGN KEY ("submissionWindowId") REFERENCES "SubmissionWindow"("id") ON DELETE SET NULL ON UPDATE CASCADE;
DO $$ BEGIN
ALTER TABLE "Round" ADD CONSTRAINT "Round_juryGroupId_fkey" FOREIGN KEY ("juryGroupId") REFERENCES "JuryGroup"("id") ON DELETE SET NULL ON UPDATE CASCADE;
EXCEPTION WHEN duplicate_object THEN NULL; END $$;
DO $$ BEGIN
ALTER TABLE "Round" ADD CONSTRAINT "Round_submissionWindowId_fkey" FOREIGN KEY ("submissionWindowId") REFERENCES "SubmissionWindow"("id") ON DELETE SET NULL ON UPDATE CASCADE;
EXCEPTION WHEN duplicate_object THEN NULL; END $$;
-- ProjectRoundState -- ProjectRoundState
DO $$ BEGIN ALTER TABLE "ProjectRoundState" ADD CONSTRAINT "ProjectRoundState_projectId_fkey" FOREIGN KEY ("projectId") REFERENCES "Project"("id") ON DELETE CASCADE ON UPDATE CASCADE;
ALTER TABLE "ProjectRoundState" ADD CONSTRAINT "ProjectRoundState_projectId_fkey" FOREIGN KEY ("projectId") REFERENCES "Project"("id") ON DELETE CASCADE ON UPDATE CASCADE; ALTER TABLE "ProjectRoundState" ADD CONSTRAINT "ProjectRoundState_roundId_fkey" FOREIGN KEY ("roundId") REFERENCES "Round"("id") ON DELETE CASCADE ON UPDATE CASCADE;
EXCEPTION WHEN duplicate_object THEN NULL; END $$;
DO $$ BEGIN
ALTER TABLE "ProjectRoundState" ADD CONSTRAINT "ProjectRoundState_roundId_fkey" FOREIGN KEY ("roundId") REFERENCES "Round"("id") ON DELETE CASCADE ON UPDATE CASCADE;
EXCEPTION WHEN duplicate_object THEN NULL; END $$;
-- AdvancementRule -- AdvancementRule
DO $$ BEGIN ALTER TABLE "AdvancementRule" ADD CONSTRAINT "AdvancementRule_roundId_fkey" FOREIGN KEY ("roundId") REFERENCES "Round"("id") ON DELETE CASCADE ON UPDATE CASCADE;
ALTER TABLE "AdvancementRule" ADD CONSTRAINT "AdvancementRule_roundId_fkey" FOREIGN KEY ("roundId") REFERENCES "Round"("id") ON DELETE CASCADE ON UPDATE CASCADE;
EXCEPTION WHEN duplicate_object THEN NULL; END $$;
-- JuryGroup -- JuryGroup
DO $$ BEGIN ALTER TABLE "JuryGroup" ADD CONSTRAINT "JuryGroup_competitionId_fkey" FOREIGN KEY ("competitionId") REFERENCES "Competition"("id") ON DELETE CASCADE ON UPDATE CASCADE;
ALTER TABLE "JuryGroup" ADD CONSTRAINT "JuryGroup_competitionId_fkey" FOREIGN KEY ("competitionId") REFERENCES "Competition"("id") ON DELETE CASCADE ON UPDATE CASCADE;
EXCEPTION WHEN duplicate_object THEN NULL; END $$;
-- JuryGroupMember -- JuryGroupMember
DO $$ BEGIN ALTER TABLE "JuryGroupMember" ADD CONSTRAINT "JuryGroupMember_juryGroupId_fkey" FOREIGN KEY ("juryGroupId") REFERENCES "JuryGroup"("id") ON DELETE CASCADE ON UPDATE CASCADE;
ALTER TABLE "JuryGroupMember" ADD CONSTRAINT "JuryGroupMember_juryGroupId_fkey" FOREIGN KEY ("juryGroupId") REFERENCES "JuryGroup"("id") ON DELETE CASCADE ON UPDATE CASCADE; ALTER TABLE "JuryGroupMember" ADD CONSTRAINT "JuryGroupMember_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User"("id") ON DELETE CASCADE ON UPDATE CASCADE;
EXCEPTION WHEN duplicate_object THEN NULL; END $$;
DO $$ BEGIN
ALTER TABLE "JuryGroupMember" ADD CONSTRAINT "JuryGroupMember_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User"("id") ON DELETE CASCADE ON UPDATE CASCADE;
EXCEPTION WHEN duplicate_object THEN NULL; END $$;
-- SubmissionWindow -- SubmissionWindow
DO $$ BEGIN ALTER TABLE "SubmissionWindow" ADD CONSTRAINT "SubmissionWindow_competitionId_fkey" FOREIGN KEY ("competitionId") REFERENCES "Competition"("id") ON DELETE CASCADE ON UPDATE CASCADE;
ALTER TABLE "SubmissionWindow" ADD CONSTRAINT "SubmissionWindow_competitionId_fkey" FOREIGN KEY ("competitionId") REFERENCES "Competition"("id") ON DELETE CASCADE ON UPDATE CASCADE;
EXCEPTION WHEN duplicate_object THEN NULL; END $$;
-- SubmissionFileRequirement -- SubmissionFileRequirement
DO $$ BEGIN ALTER TABLE "SubmissionFileRequirement" ADD CONSTRAINT "SubmissionFileRequirement_submissionWindowId_fkey" FOREIGN KEY ("submissionWindowId") REFERENCES "SubmissionWindow"("id") ON DELETE CASCADE ON UPDATE CASCADE;
ALTER TABLE "SubmissionFileRequirement" ADD CONSTRAINT "SubmissionFileRequirement_submissionWindowId_fkey" FOREIGN KEY ("submissionWindowId") REFERENCES "SubmissionWindow"("id") ON DELETE CASCADE ON UPDATE CASCADE;
EXCEPTION WHEN duplicate_object THEN NULL; END $$;
-- RoundSubmissionVisibility -- RoundSubmissionVisibility
DO $$ BEGIN ALTER TABLE "RoundSubmissionVisibility" ADD CONSTRAINT "RoundSubmissionVisibility_roundId_fkey" FOREIGN KEY ("roundId") REFERENCES "Round"("id") ON DELETE CASCADE ON UPDATE CASCADE;
ALTER TABLE "RoundSubmissionVisibility" ADD CONSTRAINT "RoundSubmissionVisibility_roundId_fkey" FOREIGN KEY ("roundId") REFERENCES "Round"("id") ON DELETE CASCADE ON UPDATE CASCADE; ALTER TABLE "RoundSubmissionVisibility" ADD CONSTRAINT "RoundSubmissionVisibility_submissionWindowId_fkey" FOREIGN KEY ("submissionWindowId") REFERENCES "SubmissionWindow"("id") ON DELETE CASCADE ON UPDATE CASCADE;
EXCEPTION WHEN duplicate_object THEN NULL; END $$;
DO $$ BEGIN
ALTER TABLE "RoundSubmissionVisibility" ADD CONSTRAINT "RoundSubmissionVisibility_submissionWindowId_fkey" FOREIGN KEY ("submissionWindowId") REFERENCES "SubmissionWindow"("id") ON DELETE CASCADE ON UPDATE CASCADE;
EXCEPTION WHEN duplicate_object THEN NULL; END $$;
-- AssignmentIntent -- AssignmentIntent
DO $$ BEGIN ALTER TABLE "AssignmentIntent" ADD CONSTRAINT "AssignmentIntent_juryGroupMemberId_fkey" FOREIGN KEY ("juryGroupMemberId") REFERENCES "JuryGroupMember"("id") ON DELETE CASCADE ON UPDATE CASCADE;
ALTER TABLE "AssignmentIntent" ADD CONSTRAINT "AssignmentIntent_juryGroupMemberId_fkey" FOREIGN KEY ("juryGroupMemberId") REFERENCES "JuryGroupMember"("id") ON DELETE CASCADE ON UPDATE CASCADE; ALTER TABLE "AssignmentIntent" ADD CONSTRAINT "AssignmentIntent_roundId_fkey" FOREIGN KEY ("roundId") REFERENCES "Round"("id") ON DELETE CASCADE ON UPDATE CASCADE;
EXCEPTION WHEN duplicate_object THEN NULL; END $$; ALTER TABLE "AssignmentIntent" ADD CONSTRAINT "AssignmentIntent_projectId_fkey" FOREIGN KEY ("projectId") REFERENCES "Project"("id") ON DELETE CASCADE ON UPDATE CASCADE;
DO $$ BEGIN
ALTER TABLE "AssignmentIntent" ADD CONSTRAINT "AssignmentIntent_roundId_fkey" FOREIGN KEY ("roundId") REFERENCES "Round"("id") ON DELETE CASCADE ON UPDATE CASCADE;
EXCEPTION WHEN duplicate_object THEN NULL; END $$;
DO $$ BEGIN
ALTER TABLE "AssignmentIntent" ADD CONSTRAINT "AssignmentIntent_projectId_fkey" FOREIGN KEY ("projectId") REFERENCES "Project"("id") ON DELETE CASCADE ON UPDATE CASCADE;
EXCEPTION WHEN duplicate_object THEN NULL; END $$;
-- AssignmentException -- AssignmentException
DO $$ BEGIN ALTER TABLE "AssignmentException" ADD CONSTRAINT "AssignmentException_assignmentId_fkey" FOREIGN KEY ("assignmentId") REFERENCES "Assignment"("id") ON DELETE CASCADE ON UPDATE CASCADE;
ALTER TABLE "AssignmentException" ADD CONSTRAINT "AssignmentException_assignmentId_fkey" FOREIGN KEY ("assignmentId") REFERENCES "Assignment"("id") ON DELETE CASCADE ON UPDATE CASCADE; ALTER TABLE "AssignmentException" ADD CONSTRAINT "AssignmentException_approvedById_fkey" FOREIGN KEY ("approvedById") REFERENCES "User"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
EXCEPTION WHEN duplicate_object THEN NULL; END $$;
DO $$ BEGIN
ALTER TABLE "AssignmentException" ADD CONSTRAINT "AssignmentException_approvedById_fkey" FOREIGN KEY ("approvedById") REFERENCES "User"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
EXCEPTION WHEN duplicate_object THEN NULL; END $$;
-- MentorFile -- MentorFile
DO $$ BEGIN ALTER TABLE "MentorFile" ADD CONSTRAINT "MentorFile_mentorAssignmentId_fkey" FOREIGN KEY ("mentorAssignmentId") REFERENCES "MentorAssignment"("id") ON DELETE CASCADE ON UPDATE CASCADE;
ALTER TABLE "MentorFile" ADD CONSTRAINT "MentorFile_mentorAssignmentId_fkey" FOREIGN KEY ("mentorAssignmentId") REFERENCES "MentorAssignment"("id") ON DELETE CASCADE ON UPDATE CASCADE; ALTER TABLE "MentorFile" ADD CONSTRAINT "MentorFile_uploadedByUserId_fkey" FOREIGN KEY ("uploadedByUserId") REFERENCES "User"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
EXCEPTION WHEN duplicate_object THEN NULL; END $$; ALTER TABLE "MentorFile" ADD CONSTRAINT "MentorFile_promotedByUserId_fkey" FOREIGN KEY ("promotedByUserId") REFERENCES "User"("id") ON DELETE SET NULL ON UPDATE CASCADE;
DO $$ BEGIN ALTER TABLE "MentorFile" ADD CONSTRAINT "MentorFile_promotedToFileId_fkey" FOREIGN KEY ("promotedToFileId") REFERENCES "ProjectFile"("id") ON DELETE SET NULL ON UPDATE CASCADE;
ALTER TABLE "MentorFile" ADD CONSTRAINT "MentorFile_uploadedByUserId_fkey" FOREIGN KEY ("uploadedByUserId") REFERENCES "User"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
EXCEPTION WHEN duplicate_object THEN NULL; END $$;
DO $$ BEGIN
ALTER TABLE "MentorFile" ADD CONSTRAINT "MentorFile_promotedByUserId_fkey" FOREIGN KEY ("promotedByUserId") REFERENCES "User"("id") ON DELETE SET NULL ON UPDATE CASCADE;
EXCEPTION WHEN duplicate_object THEN NULL; END $$;
DO $$ BEGIN
ALTER TABLE "MentorFile" ADD CONSTRAINT "MentorFile_promotedToFileId_fkey" FOREIGN KEY ("promotedToFileId") REFERENCES "ProjectFile"("id") ON DELETE SET NULL ON UPDATE CASCADE;
EXCEPTION WHEN duplicate_object THEN NULL; END $$;
-- MentorFileComment -- MentorFileComment
DO $$ BEGIN ALTER TABLE "MentorFileComment" ADD CONSTRAINT "MentorFileComment_mentorFileId_fkey" FOREIGN KEY ("mentorFileId") REFERENCES "MentorFile"("id") ON DELETE CASCADE ON UPDATE CASCADE;
ALTER TABLE "MentorFileComment" ADD CONSTRAINT "MentorFileComment_mentorFileId_fkey" FOREIGN KEY ("mentorFileId") REFERENCES "MentorFile"("id") ON DELETE CASCADE ON UPDATE CASCADE; ALTER TABLE "MentorFileComment" ADD CONSTRAINT "MentorFileComment_authorId_fkey" FOREIGN KEY ("authorId") REFERENCES "User"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
EXCEPTION WHEN duplicate_object THEN NULL; END $$; ALTER TABLE "MentorFileComment" ADD CONSTRAINT "MentorFileComment_parentCommentId_fkey" FOREIGN KEY ("parentCommentId") REFERENCES "MentorFileComment"("id") ON DELETE CASCADE ON UPDATE CASCADE;
DO $$ BEGIN
ALTER TABLE "MentorFileComment" ADD CONSTRAINT "MentorFileComment_authorId_fkey" FOREIGN KEY ("authorId") REFERENCES "User"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
EXCEPTION WHEN duplicate_object THEN NULL; END $$;
DO $$ BEGIN
ALTER TABLE "MentorFileComment" ADD CONSTRAINT "MentorFileComment_parentCommentId_fkey" FOREIGN KEY ("parentCommentId") REFERENCES "MentorFileComment"("id") ON DELETE CASCADE ON UPDATE CASCADE;
EXCEPTION WHEN duplicate_object THEN NULL; END $$;
-- SubmissionPromotionEvent -- SubmissionPromotionEvent
DO $$ BEGIN ALTER TABLE "SubmissionPromotionEvent" ADD CONSTRAINT "SubmissionPromotionEvent_projectId_fkey" FOREIGN KEY ("projectId") REFERENCES "Project"("id") ON DELETE CASCADE ON UPDATE CASCADE;
ALTER TABLE "SubmissionPromotionEvent" ADD CONSTRAINT "SubmissionPromotionEvent_projectId_fkey" FOREIGN KEY ("projectId") REFERENCES "Project"("id") ON DELETE CASCADE ON UPDATE CASCADE; ALTER TABLE "SubmissionPromotionEvent" ADD CONSTRAINT "SubmissionPromotionEvent_roundId_fkey" FOREIGN KEY ("roundId") REFERENCES "Round"("id") ON DELETE CASCADE ON UPDATE CASCADE;
EXCEPTION WHEN duplicate_object THEN NULL; END $$; ALTER TABLE "SubmissionPromotionEvent" ADD CONSTRAINT "SubmissionPromotionEvent_sourceFileId_fkey" FOREIGN KEY ("sourceFileId") REFERENCES "MentorFile"("id") ON DELETE SET NULL ON UPDATE CASCADE;
DO $$ BEGIN ALTER TABLE "SubmissionPromotionEvent" ADD CONSTRAINT "SubmissionPromotionEvent_promotedById_fkey" FOREIGN KEY ("promotedById") REFERENCES "User"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
ALTER TABLE "SubmissionPromotionEvent" ADD CONSTRAINT "SubmissionPromotionEvent_roundId_fkey" FOREIGN KEY ("roundId") REFERENCES "Round"("id") ON DELETE CASCADE ON UPDATE CASCADE;
EXCEPTION WHEN duplicate_object THEN NULL; END $$;
DO $$ BEGIN
ALTER TABLE "SubmissionPromotionEvent" ADD CONSTRAINT "SubmissionPromotionEvent_sourceFileId_fkey" FOREIGN KEY ("sourceFileId") REFERENCES "MentorFile"("id") ON DELETE SET NULL ON UPDATE CASCADE;
EXCEPTION WHEN duplicate_object THEN NULL; END $$;
DO $$ BEGIN
ALTER TABLE "SubmissionPromotionEvent" ADD CONSTRAINT "SubmissionPromotionEvent_promotedById_fkey" FOREIGN KEY ("promotedById") REFERENCES "User"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
EXCEPTION WHEN duplicate_object THEN NULL; END $$;
-- DeliberationSession -- DeliberationSession
DO $$ BEGIN ALTER TABLE "DeliberationSession" ADD CONSTRAINT "DeliberationSession_competitionId_fkey" FOREIGN KEY ("competitionId") REFERENCES "Competition"("id") ON DELETE CASCADE ON UPDATE CASCADE;
ALTER TABLE "DeliberationSession" ADD CONSTRAINT "DeliberationSession_competitionId_fkey" FOREIGN KEY ("competitionId") REFERENCES "Competition"("id") ON DELETE CASCADE ON UPDATE CASCADE; ALTER TABLE "DeliberationSession" ADD CONSTRAINT "DeliberationSession_roundId_fkey" FOREIGN KEY ("roundId") REFERENCES "Round"("id") ON DELETE CASCADE ON UPDATE CASCADE;
EXCEPTION WHEN duplicate_object THEN NULL; END $$;
DO $$ BEGIN
ALTER TABLE "DeliberationSession" ADD CONSTRAINT "DeliberationSession_roundId_fkey" FOREIGN KEY ("roundId") REFERENCES "Round"("id") ON DELETE CASCADE ON UPDATE CASCADE;
EXCEPTION WHEN duplicate_object THEN NULL; END $$;
-- DeliberationVote -- DeliberationVote
DO $$ BEGIN ALTER TABLE "DeliberationVote" ADD CONSTRAINT "DeliberationVote_sessionId_fkey" FOREIGN KEY ("sessionId") REFERENCES "DeliberationSession"("id") ON DELETE CASCADE ON UPDATE CASCADE;
ALTER TABLE "DeliberationVote" ADD CONSTRAINT "DeliberationVote_sessionId_fkey" FOREIGN KEY ("sessionId") REFERENCES "DeliberationSession"("id") ON DELETE CASCADE ON UPDATE CASCADE; ALTER TABLE "DeliberationVote" ADD CONSTRAINT "DeliberationVote_juryMemberId_fkey" FOREIGN KEY ("juryMemberId") REFERENCES "JuryGroupMember"("id") ON DELETE CASCADE ON UPDATE CASCADE;
EXCEPTION WHEN duplicate_object THEN NULL; END $$; ALTER TABLE "DeliberationVote" ADD CONSTRAINT "DeliberationVote_projectId_fkey" FOREIGN KEY ("projectId") REFERENCES "Project"("id") ON DELETE CASCADE ON UPDATE CASCADE;
DO $$ BEGIN
ALTER TABLE "DeliberationVote" ADD CONSTRAINT "DeliberationVote_juryMemberId_fkey" FOREIGN KEY ("juryMemberId") REFERENCES "JuryGroupMember"("id") ON DELETE CASCADE ON UPDATE CASCADE;
EXCEPTION WHEN duplicate_object THEN NULL; END $$;
DO $$ BEGIN
ALTER TABLE "DeliberationVote" ADD CONSTRAINT "DeliberationVote_projectId_fkey" FOREIGN KEY ("projectId") REFERENCES "Project"("id") ON DELETE CASCADE ON UPDATE CASCADE;
EXCEPTION WHEN duplicate_object THEN NULL; END $$;
-- DeliberationResult -- DeliberationResult
DO $$ BEGIN ALTER TABLE "DeliberationResult" ADD CONSTRAINT "DeliberationResult_sessionId_fkey" FOREIGN KEY ("sessionId") REFERENCES "DeliberationSession"("id") ON DELETE CASCADE ON UPDATE CASCADE;
ALTER TABLE "DeliberationResult" ADD CONSTRAINT "DeliberationResult_sessionId_fkey" FOREIGN KEY ("sessionId") REFERENCES "DeliberationSession"("id") ON DELETE CASCADE ON UPDATE CASCADE; ALTER TABLE "DeliberationResult" ADD CONSTRAINT "DeliberationResult_projectId_fkey" FOREIGN KEY ("projectId") REFERENCES "Project"("id") ON DELETE CASCADE ON UPDATE CASCADE;
EXCEPTION WHEN duplicate_object THEN NULL; END $$;
DO $$ BEGIN
ALTER TABLE "DeliberationResult" ADD CONSTRAINT "DeliberationResult_projectId_fkey" FOREIGN KEY ("projectId") REFERENCES "Project"("id") ON DELETE CASCADE ON UPDATE CASCADE;
EXCEPTION WHEN duplicate_object THEN NULL; END $$;
-- DeliberationParticipant -- DeliberationParticipant
DO $$ BEGIN ALTER TABLE "DeliberationParticipant" ADD CONSTRAINT "DeliberationParticipant_sessionId_fkey" FOREIGN KEY ("sessionId") REFERENCES "DeliberationSession"("id") ON DELETE CASCADE ON UPDATE CASCADE;
ALTER TABLE "DeliberationParticipant" ADD CONSTRAINT "DeliberationParticipant_sessionId_fkey" FOREIGN KEY ("sessionId") REFERENCES "DeliberationSession"("id") ON DELETE CASCADE ON UPDATE CASCADE; ALTER TABLE "DeliberationParticipant" ADD CONSTRAINT "DeliberationParticipant_userId_fkey" FOREIGN KEY ("userId") REFERENCES "JuryGroupMember"("id") ON DELETE CASCADE ON UPDATE CASCADE;
EXCEPTION WHEN duplicate_object THEN NULL; END $$; ALTER TABLE "DeliberationParticipant" ADD CONSTRAINT "DeliberationParticipant_replacedById_fkey" FOREIGN KEY ("replacedById") REFERENCES "User"("id") ON DELETE SET NULL ON UPDATE CASCADE;
DO $$ BEGIN
ALTER TABLE "DeliberationParticipant" ADD CONSTRAINT "DeliberationParticipant_userId_fkey" FOREIGN KEY ("userId") REFERENCES "JuryGroupMember"("id") ON DELETE CASCADE ON UPDATE CASCADE;
EXCEPTION WHEN duplicate_object THEN NULL; END $$;
DO $$ BEGIN
ALTER TABLE "DeliberationParticipant" ADD CONSTRAINT "DeliberationParticipant_replacedById_fkey" FOREIGN KEY ("replacedById") REFERENCES "User"("id") ON DELETE SET NULL ON UPDATE CASCADE;
EXCEPTION WHEN duplicate_object THEN NULL; END $$;
-- ResultLock -- ResultLock
DO $$ BEGIN ALTER TABLE "ResultLock" ADD CONSTRAINT "ResultLock_competitionId_fkey" FOREIGN KEY ("competitionId") REFERENCES "Competition"("id") ON DELETE CASCADE ON UPDATE CASCADE;
ALTER TABLE "ResultLock" ADD CONSTRAINT "ResultLock_competitionId_fkey" FOREIGN KEY ("competitionId") REFERENCES "Competition"("id") ON DELETE CASCADE ON UPDATE CASCADE; ALTER TABLE "ResultLock" ADD CONSTRAINT "ResultLock_roundId_fkey" FOREIGN KEY ("roundId") REFERENCES "Round"("id") ON DELETE CASCADE ON UPDATE CASCADE;
EXCEPTION WHEN duplicate_object THEN NULL; END $$; ALTER TABLE "ResultLock" ADD CONSTRAINT "ResultLock_lockedById_fkey" FOREIGN KEY ("lockedById") REFERENCES "User"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
DO $$ BEGIN
ALTER TABLE "ResultLock" ADD CONSTRAINT "ResultLock_roundId_fkey" FOREIGN KEY ("roundId") REFERENCES "Round"("id") ON DELETE CASCADE ON UPDATE CASCADE;
EXCEPTION WHEN duplicate_object THEN NULL; END $$;
DO $$ BEGIN
ALTER TABLE "ResultLock" ADD CONSTRAINT "ResultLock_lockedById_fkey" FOREIGN KEY ("lockedById") REFERENCES "User"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
EXCEPTION WHEN duplicate_object THEN NULL; END $$;
-- ResultUnlockEvent -- ResultUnlockEvent
DO $$ BEGIN ALTER TABLE "ResultUnlockEvent" ADD CONSTRAINT "ResultUnlockEvent_resultLockId_fkey" FOREIGN KEY ("resultLockId") REFERENCES "ResultLock"("id") ON DELETE CASCADE ON UPDATE CASCADE;
ALTER TABLE "ResultUnlockEvent" ADD CONSTRAINT "ResultUnlockEvent_resultLockId_fkey" FOREIGN KEY ("resultLockId") REFERENCES "ResultLock"("id") ON DELETE CASCADE ON UPDATE CASCADE; ALTER TABLE "ResultUnlockEvent" ADD CONSTRAINT "ResultUnlockEvent_unlockedById_fkey" FOREIGN KEY ("unlockedById") REFERENCES "User"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
EXCEPTION WHEN duplicate_object THEN NULL; END $$;
DO $$ BEGIN
ALTER TABLE "ResultUnlockEvent" ADD CONSTRAINT "ResultUnlockEvent_unlockedById_fkey" FOREIGN KEY ("unlockedById") REFERENCES "User"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
EXCEPTION WHEN duplicate_object THEN NULL; END $$;
-- FKs on modified existing tables -- FKs on modified existing tables
DO $$ BEGIN ALTER TABLE "Assignment" ADD CONSTRAINT "Assignment_juryGroupId_fkey" FOREIGN KEY ("juryGroupId") REFERENCES "JuryGroup"("id") ON DELETE SET NULL ON UPDATE CASCADE;
ALTER TABLE "Assignment" ADD CONSTRAINT "Assignment_juryGroupId_fkey" FOREIGN KEY ("juryGroupId") REFERENCES "JuryGroup"("id") ON DELETE SET NULL ON UPDATE CASCADE; ALTER TABLE "SpecialAward" ADD CONSTRAINT "SpecialAward_competitionId_fkey" FOREIGN KEY ("competitionId") REFERENCES "Competition"("id") ON DELETE SET NULL ON UPDATE CASCADE;
EXCEPTION WHEN duplicate_object THEN NULL; END $$; ALTER TABLE "SpecialAward" ADD CONSTRAINT "SpecialAward_evaluationRoundId_fkey" FOREIGN KEY ("evaluationRoundId") REFERENCES "Round"("id") ON DELETE SET NULL ON UPDATE CASCADE;
DO $$ BEGIN ALTER TABLE "SpecialAward" ADD CONSTRAINT "SpecialAward_juryGroupId_fkey" FOREIGN KEY ("juryGroupId") REFERENCES "JuryGroup"("id") ON DELETE SET NULL ON UPDATE CASCADE;
ALTER TABLE "SpecialAward" ADD CONSTRAINT "SpecialAward_competitionId_fkey" FOREIGN KEY ("competitionId") REFERENCES "Competition"("id") ON DELETE SET NULL ON UPDATE CASCADE; ALTER TABLE "MentorMessage" ADD CONSTRAINT "MentorMessage_workspaceId_fkey" FOREIGN KEY ("workspaceId") REFERENCES "MentorAssignment"("id") ON DELETE CASCADE ON UPDATE CASCADE;
EXCEPTION WHEN duplicate_object THEN NULL; END $$; ALTER TABLE "ProjectFile" ADD CONSTRAINT "ProjectFile_submissionWindowId_fkey" FOREIGN KEY ("submissionWindowId") REFERENCES "SubmissionWindow"("id") ON DELETE SET NULL ON UPDATE CASCADE;
DO $$ BEGIN ALTER TABLE "ProjectFile" ADD CONSTRAINT "ProjectFile_submissionFileRequirementId_fkey" FOREIGN KEY ("submissionFileRequirementId") REFERENCES "SubmissionFileRequirement"("id") ON DELETE SET NULL ON UPDATE CASCADE;
ALTER TABLE "SpecialAward" ADD CONSTRAINT "SpecialAward_evaluationRoundId_fkey" FOREIGN KEY ("evaluationRoundId") REFERENCES "Round"("id") ON DELETE SET NULL ON UPDATE CASCADE;
EXCEPTION WHEN duplicate_object THEN NULL; END $$;
DO $$ BEGIN
ALTER TABLE "SpecialAward" ADD CONSTRAINT "SpecialAward_juryGroupId_fkey" FOREIGN KEY ("juryGroupId") REFERENCES "JuryGroup"("id") ON DELETE SET NULL ON UPDATE CASCADE;
EXCEPTION WHEN duplicate_object THEN NULL; END $$;
DO $$ BEGIN
ALTER TABLE "MentorMessage" ADD CONSTRAINT "MentorMessage_workspaceId_fkey" FOREIGN KEY ("workspaceId") REFERENCES "MentorAssignment"("id") ON DELETE CASCADE ON UPDATE CASCADE;
EXCEPTION WHEN duplicate_object THEN NULL; END $$;
DO $$ BEGIN
ALTER TABLE "ProjectFile" ADD CONSTRAINT "ProjectFile_submissionWindowId_fkey" FOREIGN KEY ("submissionWindowId") REFERENCES "SubmissionWindow"("id") ON DELETE SET NULL ON UPDATE CASCADE;
EXCEPTION WHEN duplicate_object THEN NULL; END $$;
DO $$ BEGIN
ALTER TABLE "ProjectFile" ADD CONSTRAINT "ProjectFile_submissionFileRequirementId_fkey" FOREIGN KEY ("submissionFileRequirementId") REFERENCES "SubmissionFileRequirement"("id") ON DELETE SET NULL ON UPDATE CASCADE;
EXCEPTION WHEN duplicate_object THEN NULL; END $$;

View File

@ -1,7 +1,3 @@
-- AlterTable -- AlterTable
DO $$ BEGIN ALTER TABLE "JuryGroupMember" ADD COLUMN "selfServiceCap" INTEGER,
ALTER TABLE "JuryGroupMember" ADD COLUMN "selfServiceCap" INTEGER; ADD COLUMN "selfServiceRatio" DOUBLE PRECISION;
EXCEPTION WHEN duplicate_column THEN NULL; END $$;
DO $$ BEGIN
ALTER TABLE "JuryGroupMember" ADD COLUMN "selfServiceRatio" DOUBLE PRECISION;
EXCEPTION WHEN duplicate_column THEN NULL; END $$;

View File

@ -1,14 +1,14 @@
-- ============================================================================= -- =============================================================================
-- Phase 7/8 Migration Part 1: Rename stageId -> roundId on 15 tables -- Phase 7/8 Migration Part 1: Rename stageId roundId on 15 tables
-- ============================================================================= -- =============================================================================
-- This migration renames stageId columns to roundId and updates FK constraints -- This migration renames stageId columns to roundId and updates FK constraints
-- to point to the Round table instead of Stage table. -- to point to the Round table instead of Stage table.
-- --
-- NOTE: After the pipeline migration (20260213), most tables have BOTH a -- NOTE: After the pipeline migration (20260213), most tables have BOTH a
-- nullable roundId column (legacy, no FK) AND a stageId column. We must -- nullable roundId column (legacy, no FK) AND a stageId column. We must
-- drop the old roundId column before renaming stageId -> roundId. -- drop the old roundId column before renaming stageId roundId.
-- --- 1. EvaluationForm --- -- ─── 1. EvaluationForm ───────────────────────────────────────────────────────
-- Drop old roundId column (nullable, no FK since 20260213 migration) -- Drop old roundId column (nullable, no FK since 20260213 migration)
ALTER TABLE "EvaluationForm" DROP COLUMN IF EXISTS "roundId"; ALTER TABLE "EvaluationForm" DROP COLUMN IF EXISTS "roundId";
@ -20,22 +20,18 @@ ALTER TABLE "EvaluationForm" DROP CONSTRAINT IF EXISTS "EvaluationForm_stageId_f
DROP INDEX IF EXISTS "EvaluationForm_stageId_version_key"; DROP INDEX IF EXISTS "EvaluationForm_stageId_version_key";
DROP INDEX IF EXISTS "EvaluationForm_stageId_isActive_idx"; DROP INDEX IF EXISTS "EvaluationForm_stageId_isActive_idx";
-- Rename column (only if stageId exists) -- Rename column
DO $$ BEGIN ALTER TABLE "EvaluationForm" RENAME COLUMN "stageId" TO "roundId";
ALTER TABLE "EvaluationForm" RENAME COLUMN "stageId" TO "roundId";
EXCEPTION WHEN undefined_column THEN NULL; END $$;
-- Recreate indexes with new name -- Recreate indexes with new name
CREATE UNIQUE INDEX IF NOT EXISTS "EvaluationForm_roundId_version_key" ON "EvaluationForm"("roundId", "version"); CREATE UNIQUE INDEX "EvaluationForm_roundId_version_key" ON "EvaluationForm"("roundId", "version");
CREATE INDEX IF NOT EXISTS "EvaluationForm_roundId_isActive_idx" ON "EvaluationForm"("roundId", "isActive"); CREATE INDEX "EvaluationForm_roundId_isActive_idx" ON "EvaluationForm"("roundId", "isActive");
-- Recreate FK pointing to Round -- Recreate FK pointing to Round
DO $$ BEGIN ALTER TABLE "EvaluationForm" ADD CONSTRAINT "EvaluationForm_roundId_fkey"
ALTER TABLE "EvaluationForm" ADD CONSTRAINT "EvaluationForm_roundId_fkey"
FOREIGN KEY ("roundId") REFERENCES "Round"("id") ON DELETE CASCADE ON UPDATE CASCADE; FOREIGN KEY ("roundId") REFERENCES "Round"("id") ON DELETE CASCADE ON UPDATE CASCADE;
EXCEPTION WHEN duplicate_object THEN NULL; END $$;
-- --- 2. FileRequirement --- -- ─── 2. FileRequirement ──────────────────────────────────────────────────────
ALTER TABLE "FileRequirement" DROP COLUMN IF EXISTS "roundId"; ALTER TABLE "FileRequirement" DROP COLUMN IF EXISTS "roundId";
@ -43,18 +39,14 @@ ALTER TABLE "FileRequirement" DROP CONSTRAINT IF EXISTS "FileRequirement_stageId
DROP INDEX IF EXISTS "FileRequirement_stageId_idx"; DROP INDEX IF EXISTS "FileRequirement_stageId_idx";
DO $$ BEGIN ALTER TABLE "FileRequirement" RENAME COLUMN "stageId" TO "roundId";
ALTER TABLE "FileRequirement" RENAME COLUMN "stageId" TO "roundId";
EXCEPTION WHEN undefined_column THEN NULL; END $$;
CREATE INDEX IF NOT EXISTS "FileRequirement_roundId_idx" ON "FileRequirement"("roundId"); CREATE INDEX "FileRequirement_roundId_idx" ON "FileRequirement"("roundId");
DO $$ BEGIN ALTER TABLE "FileRequirement" ADD CONSTRAINT "FileRequirement_roundId_fkey"
ALTER TABLE "FileRequirement" ADD CONSTRAINT "FileRequirement_roundId_fkey"
FOREIGN KEY ("roundId") REFERENCES "Round"("id") ON DELETE CASCADE ON UPDATE CASCADE; FOREIGN KEY ("roundId") REFERENCES "Round"("id") ON DELETE CASCADE ON UPDATE CASCADE;
EXCEPTION WHEN duplicate_object THEN NULL; END $$;
-- --- 3. Assignment --- -- ─── 3. Assignment ───────────────────────────────────────────────────────────
ALTER TABLE "Assignment" DROP COLUMN IF EXISTS "roundId"; ALTER TABLE "Assignment" DROP COLUMN IF EXISTS "roundId";
@ -63,19 +55,15 @@ ALTER TABLE "Assignment" DROP CONSTRAINT IF EXISTS "Assignment_stageId_fkey";
DROP INDEX IF EXISTS "Assignment_userId_projectId_stageId_key"; DROP INDEX IF EXISTS "Assignment_userId_projectId_stageId_key";
DROP INDEX IF EXISTS "Assignment_stageId_idx"; DROP INDEX IF EXISTS "Assignment_stageId_idx";
DO $$ BEGIN ALTER TABLE "Assignment" RENAME COLUMN "stageId" TO "roundId";
ALTER TABLE "Assignment" RENAME COLUMN "stageId" TO "roundId";
EXCEPTION WHEN undefined_column THEN NULL; END $$;
CREATE UNIQUE INDEX IF NOT EXISTS "Assignment_userId_projectId_roundId_key" ON "Assignment"("userId", "projectId", "roundId"); CREATE UNIQUE INDEX "Assignment_userId_projectId_roundId_key" ON "Assignment"("userId", "projectId", "roundId");
CREATE INDEX IF NOT EXISTS "Assignment_roundId_idx" ON "Assignment"("roundId"); CREATE INDEX "Assignment_roundId_idx" ON "Assignment"("roundId");
DO $$ BEGIN ALTER TABLE "Assignment" ADD CONSTRAINT "Assignment_roundId_fkey"
ALTER TABLE "Assignment" ADD CONSTRAINT "Assignment_roundId_fkey"
FOREIGN KEY ("roundId") REFERENCES "Round"("id") ON DELETE CASCADE ON UPDATE CASCADE; FOREIGN KEY ("roundId") REFERENCES "Round"("id") ON DELETE CASCADE ON UPDATE CASCADE;
EXCEPTION WHEN duplicate_object THEN NULL; END $$;
-- --- 4. GracePeriod --- -- ─── 4. GracePeriod ──────────────────────────────────────────────────────────
ALTER TABLE "GracePeriod" DROP COLUMN IF EXISTS "roundId"; ALTER TABLE "GracePeriod" DROP COLUMN IF EXISTS "roundId";
@ -84,19 +72,15 @@ ALTER TABLE "GracePeriod" DROP CONSTRAINT IF EXISTS "GracePeriod_stageId_fkey";
DROP INDEX IF EXISTS "GracePeriod_stageId_idx"; DROP INDEX IF EXISTS "GracePeriod_stageId_idx";
DROP INDEX IF EXISTS "GracePeriod_stageId_userId_extendedUntil_idx"; DROP INDEX IF EXISTS "GracePeriod_stageId_userId_extendedUntil_idx";
DO $$ BEGIN ALTER TABLE "GracePeriod" RENAME COLUMN "stageId" TO "roundId";
ALTER TABLE "GracePeriod" RENAME COLUMN "stageId" TO "roundId";
EXCEPTION WHEN undefined_column THEN NULL; END $$;
CREATE INDEX IF NOT EXISTS "GracePeriod_roundId_idx" ON "GracePeriod"("roundId"); CREATE INDEX "GracePeriod_roundId_idx" ON "GracePeriod"("roundId");
CREATE INDEX IF NOT EXISTS "GracePeriod_roundId_userId_extendedUntil_idx" ON "GracePeriod"("roundId", "userId", "extendedUntil"); CREATE INDEX "GracePeriod_roundId_userId_extendedUntil_idx" ON "GracePeriod"("roundId", "userId", "extendedUntil");
DO $$ BEGIN ALTER TABLE "GracePeriod" ADD CONSTRAINT "GracePeriod_roundId_fkey"
ALTER TABLE "GracePeriod" ADD CONSTRAINT "GracePeriod_roundId_fkey"
FOREIGN KEY ("roundId") REFERENCES "Round"("id") ON DELETE CASCADE ON UPDATE CASCADE; FOREIGN KEY ("roundId") REFERENCES "Round"("id") ON DELETE CASCADE ON UPDATE CASCADE;
EXCEPTION WHEN duplicate_object THEN NULL; END $$;
-- --- 5. LiveVotingSession --- -- ─── 5. LiveVotingSession ────────────────────────────────────────────────────
ALTER TABLE "LiveVotingSession" DROP COLUMN IF EXISTS "roundId"; ALTER TABLE "LiveVotingSession" DROP COLUMN IF EXISTS "roundId";
@ -104,18 +88,14 @@ ALTER TABLE "LiveVotingSession" DROP CONSTRAINT IF EXISTS "LiveVotingSession_sta
DROP INDEX IF EXISTS "LiveVotingSession_stageId_key"; DROP INDEX IF EXISTS "LiveVotingSession_stageId_key";
DO $$ BEGIN ALTER TABLE "LiveVotingSession" RENAME COLUMN "stageId" TO "roundId";
ALTER TABLE "LiveVotingSession" RENAME COLUMN "stageId" TO "roundId";
EXCEPTION WHEN undefined_column THEN NULL; END $$;
CREATE UNIQUE INDEX IF NOT EXISTS "LiveVotingSession_roundId_key" ON "LiveVotingSession"("roundId"); CREATE UNIQUE INDEX "LiveVotingSession_roundId_key" ON "LiveVotingSession"("roundId");
DO $$ BEGIN ALTER TABLE "LiveVotingSession" ADD CONSTRAINT "LiveVotingSession_roundId_fkey"
ALTER TABLE "LiveVotingSession" ADD CONSTRAINT "LiveVotingSession_roundId_fkey"
FOREIGN KEY ("roundId") REFERENCES "Round"("id") ON DELETE CASCADE ON UPDATE CASCADE; FOREIGN KEY ("roundId") REFERENCES "Round"("id") ON DELETE CASCADE ON UPDATE CASCADE;
EXCEPTION WHEN duplicate_object THEN NULL; END $$;
-- --- 6. FilteringRule --- -- ─── 6. FilteringRule ────────────────────────────────────────────────────────
ALTER TABLE "FilteringRule" DROP COLUMN IF EXISTS "roundId"; ALTER TABLE "FilteringRule" DROP COLUMN IF EXISTS "roundId";
@ -123,18 +103,14 @@ ALTER TABLE "FilteringRule" DROP CONSTRAINT IF EXISTS "FilteringRule_stageId_fke
DROP INDEX IF EXISTS "FilteringRule_stageId_idx"; DROP INDEX IF EXISTS "FilteringRule_stageId_idx";
DO $$ BEGIN ALTER TABLE "FilteringRule" RENAME COLUMN "stageId" TO "roundId";
ALTER TABLE "FilteringRule" RENAME COLUMN "stageId" TO "roundId";
EXCEPTION WHEN undefined_column THEN NULL; END $$;
CREATE INDEX IF NOT EXISTS "FilteringRule_roundId_idx" ON "FilteringRule"("roundId"); CREATE INDEX "FilteringRule_roundId_idx" ON "FilteringRule"("roundId");
DO $$ BEGIN ALTER TABLE "FilteringRule" ADD CONSTRAINT "FilteringRule_roundId_fkey"
ALTER TABLE "FilteringRule" ADD CONSTRAINT "FilteringRule_roundId_fkey"
FOREIGN KEY ("roundId") REFERENCES "Round"("id") ON DELETE CASCADE ON UPDATE CASCADE; FOREIGN KEY ("roundId") REFERENCES "Round"("id") ON DELETE CASCADE ON UPDATE CASCADE;
EXCEPTION WHEN duplicate_object THEN NULL; END $$;
-- --- 7. FilteringResult --- -- ─── 7. FilteringResult ──────────────────────────────────────────────────────
ALTER TABLE "FilteringResult" DROP COLUMN IF EXISTS "roundId"; ALTER TABLE "FilteringResult" DROP COLUMN IF EXISTS "roundId";
@ -143,19 +119,15 @@ ALTER TABLE "FilteringResult" DROP CONSTRAINT IF EXISTS "FilteringResult_stageId
DROP INDEX IF EXISTS "FilteringResult_stageId_projectId_key"; DROP INDEX IF EXISTS "FilteringResult_stageId_projectId_key";
DROP INDEX IF EXISTS "FilteringResult_stageId_idx"; DROP INDEX IF EXISTS "FilteringResult_stageId_idx";
DO $$ BEGIN ALTER TABLE "FilteringResult" RENAME COLUMN "stageId" TO "roundId";
ALTER TABLE "FilteringResult" RENAME COLUMN "stageId" TO "roundId";
EXCEPTION WHEN undefined_column THEN NULL; END $$;
CREATE UNIQUE INDEX IF NOT EXISTS "FilteringResult_roundId_projectId_key" ON "FilteringResult"("roundId", "projectId"); CREATE UNIQUE INDEX "FilteringResult_roundId_projectId_key" ON "FilteringResult"("roundId", "projectId");
CREATE INDEX IF NOT EXISTS "FilteringResult_roundId_idx" ON "FilteringResult"("roundId"); CREATE INDEX "FilteringResult_roundId_idx" ON "FilteringResult"("roundId");
DO $$ BEGIN ALTER TABLE "FilteringResult" ADD CONSTRAINT "FilteringResult_roundId_fkey"
ALTER TABLE "FilteringResult" ADD CONSTRAINT "FilteringResult_roundId_fkey"
FOREIGN KEY ("roundId") REFERENCES "Round"("id") ON DELETE CASCADE ON UPDATE CASCADE; FOREIGN KEY ("roundId") REFERENCES "Round"("id") ON DELETE CASCADE ON UPDATE CASCADE;
EXCEPTION WHEN duplicate_object THEN NULL; END $$;
-- --- 8. FilteringJob --- -- ─── 8. FilteringJob ─────────────────────────────────────────────────────────
ALTER TABLE "FilteringJob" DROP COLUMN IF EXISTS "roundId"; ALTER TABLE "FilteringJob" DROP COLUMN IF EXISTS "roundId";
@ -163,18 +135,14 @@ ALTER TABLE "FilteringJob" DROP CONSTRAINT IF EXISTS "FilteringJob_stageId_fkey"
DROP INDEX IF EXISTS "FilteringJob_stageId_idx"; DROP INDEX IF EXISTS "FilteringJob_stageId_idx";
DO $$ BEGIN ALTER TABLE "FilteringJob" RENAME COLUMN "stageId" TO "roundId";
ALTER TABLE "FilteringJob" RENAME COLUMN "stageId" TO "roundId";
EXCEPTION WHEN undefined_column THEN NULL; END $$;
CREATE INDEX IF NOT EXISTS "FilteringJob_roundId_idx" ON "FilteringJob"("roundId"); CREATE INDEX "FilteringJob_roundId_idx" ON "FilteringJob"("roundId");
DO $$ BEGIN ALTER TABLE "FilteringJob" ADD CONSTRAINT "FilteringJob_roundId_fkey"
ALTER TABLE "FilteringJob" ADD CONSTRAINT "FilteringJob_roundId_fkey"
FOREIGN KEY ("roundId") REFERENCES "Round"("id") ON DELETE CASCADE ON UPDATE CASCADE; FOREIGN KEY ("roundId") REFERENCES "Round"("id") ON DELETE CASCADE ON UPDATE CASCADE;
EXCEPTION WHEN duplicate_object THEN NULL; END $$;
-- --- 9. AssignmentJob --- -- ─── 9. AssignmentJob ────────────────────────────────────────────────────────
ALTER TABLE "AssignmentJob" DROP COLUMN IF EXISTS "roundId"; ALTER TABLE "AssignmentJob" DROP COLUMN IF EXISTS "roundId";
@ -182,18 +150,14 @@ ALTER TABLE "AssignmentJob" DROP CONSTRAINT IF EXISTS "AssignmentJob_stageId_fke
DROP INDEX IF EXISTS "AssignmentJob_stageId_idx"; DROP INDEX IF EXISTS "AssignmentJob_stageId_idx";
DO $$ BEGIN ALTER TABLE "AssignmentJob" RENAME COLUMN "stageId" TO "roundId";
ALTER TABLE "AssignmentJob" RENAME COLUMN "stageId" TO "roundId";
EXCEPTION WHEN undefined_column THEN NULL; END $$;
CREATE INDEX IF NOT EXISTS "AssignmentJob_roundId_idx" ON "AssignmentJob"("roundId"); CREATE INDEX "AssignmentJob_roundId_idx" ON "AssignmentJob"("roundId");
DO $$ BEGIN ALTER TABLE "AssignmentJob" ADD CONSTRAINT "AssignmentJob_roundId_fkey"
ALTER TABLE "AssignmentJob" ADD CONSTRAINT "AssignmentJob_roundId_fkey"
FOREIGN KEY ("roundId") REFERENCES "Round"("id") ON DELETE CASCADE ON UPDATE CASCADE; FOREIGN KEY ("roundId") REFERENCES "Round"("id") ON DELETE CASCADE ON UPDATE CASCADE;
EXCEPTION WHEN duplicate_object THEN NULL; END $$;
-- --- 10. ReminderLog --- -- ─── 10. ReminderLog ─────────────────────────────────────────────────────────
ALTER TABLE "ReminderLog" DROP COLUMN IF EXISTS "roundId"; ALTER TABLE "ReminderLog" DROP COLUMN IF EXISTS "roundId";
@ -202,19 +166,15 @@ ALTER TABLE "ReminderLog" DROP CONSTRAINT IF EXISTS "ReminderLog_stageId_fkey";
DROP INDEX IF EXISTS "ReminderLog_stageId_userId_type_key"; DROP INDEX IF EXISTS "ReminderLog_stageId_userId_type_key";
DROP INDEX IF EXISTS "ReminderLog_stageId_idx"; DROP INDEX IF EXISTS "ReminderLog_stageId_idx";
DO $$ BEGIN ALTER TABLE "ReminderLog" RENAME COLUMN "stageId" TO "roundId";
ALTER TABLE "ReminderLog" RENAME COLUMN "stageId" TO "roundId";
EXCEPTION WHEN undefined_column THEN NULL; END $$;
CREATE UNIQUE INDEX IF NOT EXISTS "ReminderLog_roundId_userId_type_key" ON "ReminderLog"("roundId", "userId", "type"); CREATE UNIQUE INDEX "ReminderLog_roundId_userId_type_key" ON "ReminderLog"("roundId", "userId", "type");
CREATE INDEX IF NOT EXISTS "ReminderLog_roundId_idx" ON "ReminderLog"("roundId"); CREATE INDEX "ReminderLog_roundId_idx" ON "ReminderLog"("roundId");
DO $$ BEGIN ALTER TABLE "ReminderLog" ADD CONSTRAINT "ReminderLog_roundId_fkey"
ALTER TABLE "ReminderLog" ADD CONSTRAINT "ReminderLog_roundId_fkey"
FOREIGN KEY ("roundId") REFERENCES "Round"("id") ON DELETE CASCADE ON UPDATE CASCADE; FOREIGN KEY ("roundId") REFERENCES "Round"("id") ON DELETE CASCADE ON UPDATE CASCADE;
EXCEPTION WHEN duplicate_object THEN NULL; END $$;
-- --- 11. EvaluationSummary --- -- ─── 11. EvaluationSummary ───────────────────────────────────────────────────
ALTER TABLE "EvaluationSummary" DROP COLUMN IF EXISTS "roundId"; ALTER TABLE "EvaluationSummary" DROP COLUMN IF EXISTS "roundId";
@ -223,19 +183,15 @@ ALTER TABLE "EvaluationSummary" DROP CONSTRAINT IF EXISTS "EvaluationSummary_sta
DROP INDEX IF EXISTS "EvaluationSummary_projectId_stageId_key"; DROP INDEX IF EXISTS "EvaluationSummary_projectId_stageId_key";
DROP INDEX IF EXISTS "EvaluationSummary_stageId_idx"; DROP INDEX IF EXISTS "EvaluationSummary_stageId_idx";
DO $$ BEGIN ALTER TABLE "EvaluationSummary" RENAME COLUMN "stageId" TO "roundId";
ALTER TABLE "EvaluationSummary" RENAME COLUMN "stageId" TO "roundId";
EXCEPTION WHEN undefined_column THEN NULL; END $$;
CREATE UNIQUE INDEX IF NOT EXISTS "EvaluationSummary_projectId_roundId_key" ON "EvaluationSummary"("projectId", "roundId"); CREATE UNIQUE INDEX "EvaluationSummary_projectId_roundId_key" ON "EvaluationSummary"("projectId", "roundId");
CREATE INDEX IF NOT EXISTS "EvaluationSummary_roundId_idx" ON "EvaluationSummary"("roundId"); CREATE INDEX "EvaluationSummary_roundId_idx" ON "EvaluationSummary"("roundId");
DO $$ BEGIN ALTER TABLE "EvaluationSummary" ADD CONSTRAINT "EvaluationSummary_roundId_fkey"
ALTER TABLE "EvaluationSummary" ADD CONSTRAINT "EvaluationSummary_roundId_fkey"
FOREIGN KEY ("roundId") REFERENCES "Round"("id") ON DELETE CASCADE ON UPDATE CASCADE; FOREIGN KEY ("roundId") REFERENCES "Round"("id") ON DELETE CASCADE ON UPDATE CASCADE;
EXCEPTION WHEN duplicate_object THEN NULL; END $$;
-- --- 12. EvaluationDiscussion --- -- ─── 12. EvaluationDiscussion ────────────────────────────────────────────────
ALTER TABLE "EvaluationDiscussion" DROP COLUMN IF EXISTS "roundId"; ALTER TABLE "EvaluationDiscussion" DROP COLUMN IF EXISTS "roundId";
@ -244,19 +200,15 @@ ALTER TABLE "EvaluationDiscussion" DROP CONSTRAINT IF EXISTS "EvaluationDiscussi
DROP INDEX IF EXISTS "EvaluationDiscussion_projectId_stageId_key"; DROP INDEX IF EXISTS "EvaluationDiscussion_projectId_stageId_key";
DROP INDEX IF EXISTS "EvaluationDiscussion_stageId_idx"; DROP INDEX IF EXISTS "EvaluationDiscussion_stageId_idx";
DO $$ BEGIN ALTER TABLE "EvaluationDiscussion" RENAME COLUMN "stageId" TO "roundId";
ALTER TABLE "EvaluationDiscussion" RENAME COLUMN "stageId" TO "roundId";
EXCEPTION WHEN undefined_column THEN NULL; END $$;
CREATE UNIQUE INDEX IF NOT EXISTS "EvaluationDiscussion_projectId_roundId_key" ON "EvaluationDiscussion"("projectId", "roundId"); CREATE UNIQUE INDEX "EvaluationDiscussion_projectId_roundId_key" ON "EvaluationDiscussion"("projectId", "roundId");
CREATE INDEX IF NOT EXISTS "EvaluationDiscussion_roundId_idx" ON "EvaluationDiscussion"("roundId"); CREATE INDEX "EvaluationDiscussion_roundId_idx" ON "EvaluationDiscussion"("roundId");
DO $$ BEGIN ALTER TABLE "EvaluationDiscussion" ADD CONSTRAINT "EvaluationDiscussion_roundId_fkey"
ALTER TABLE "EvaluationDiscussion" ADD CONSTRAINT "EvaluationDiscussion_roundId_fkey"
FOREIGN KEY ("roundId") REFERENCES "Round"("id") ON DELETE CASCADE ON UPDATE CASCADE; FOREIGN KEY ("roundId") REFERENCES "Round"("id") ON DELETE CASCADE ON UPDATE CASCADE;
EXCEPTION WHEN duplicate_object THEN NULL; END $$;
-- --- 13. Message --- -- ─── 13. Message ─────────────────────────────────────────────────────────────
-- Message has roundId (from init, nullable) and stageId (from pipeline, nullable) -- Message has roundId (from init, nullable) and stageId (from pipeline, nullable)
ALTER TABLE "Message" DROP COLUMN IF EXISTS "roundId"; ALTER TABLE "Message" DROP COLUMN IF EXISTS "roundId";
@ -265,54 +217,42 @@ ALTER TABLE "Message" DROP CONSTRAINT IF EXISTS "Message_stageId_fkey";
DROP INDEX IF EXISTS "Message_stageId_idx"; DROP INDEX IF EXISTS "Message_stageId_idx";
DO $$ BEGIN ALTER TABLE "Message" RENAME COLUMN "stageId" TO "roundId";
ALTER TABLE "Message" RENAME COLUMN "stageId" TO "roundId";
EXCEPTION WHEN undefined_column THEN NULL; END $$;
CREATE INDEX IF NOT EXISTS "Message_roundId_idx" ON "Message"("roundId"); CREATE INDEX "Message_roundId_idx" ON "Message"("roundId");
DO $$ BEGIN ALTER TABLE "Message" ADD CONSTRAINT "Message_roundId_fkey"
ALTER TABLE "Message" ADD CONSTRAINT "Message_roundId_fkey"
FOREIGN KEY ("roundId") REFERENCES "Round"("id") ON DELETE SET NULL ON UPDATE CASCADE; FOREIGN KEY ("roundId") REFERENCES "Round"("id") ON DELETE SET NULL ON UPDATE CASCADE;
EXCEPTION WHEN duplicate_object THEN NULL; END $$;
-- --- 14. Cohort --- -- ─── 14. Cohort ──────────────────────────────────────────────────────────────
-- Cohort was created in pipeline migration with stageId only (no roundId) -- Cohort was created in pipeline migration with stageId only (no roundId)
ALTER TABLE "Cohort" DROP CONSTRAINT IF EXISTS "Cohort_stageId_fkey"; ALTER TABLE "Cohort" DROP CONSTRAINT IF EXISTS "Cohort_stageId_fkey";
DROP INDEX IF EXISTS "Cohort_stageId_idx"; DROP INDEX IF EXISTS "Cohort_stageId_idx";
DO $$ BEGIN ALTER TABLE "Cohort" RENAME COLUMN "stageId" TO "roundId";
ALTER TABLE "Cohort" RENAME COLUMN "stageId" TO "roundId";
EXCEPTION WHEN undefined_column THEN NULL; END $$;
CREATE INDEX IF NOT EXISTS "Cohort_roundId_idx" ON "Cohort"("roundId"); CREATE INDEX "Cohort_roundId_idx" ON "Cohort"("roundId");
DO $$ BEGIN ALTER TABLE "Cohort" ADD CONSTRAINT "Cohort_roundId_fkey"
ALTER TABLE "Cohort" ADD CONSTRAINT "Cohort_roundId_fkey"
FOREIGN KEY ("roundId") REFERENCES "Round"("id") ON DELETE CASCADE ON UPDATE CASCADE; FOREIGN KEY ("roundId") REFERENCES "Round"("id") ON DELETE CASCADE ON UPDATE CASCADE;
EXCEPTION WHEN duplicate_object THEN NULL; END $$;
-- --- 15. LiveProgressCursor --- -- ─── 15. LiveProgressCursor ──────────────────────────────────────────────────
-- LiveProgressCursor was created in pipeline migration with stageId only (no roundId) -- LiveProgressCursor was created in pipeline migration with stageId only (no roundId)
ALTER TABLE "LiveProgressCursor" DROP CONSTRAINT IF EXISTS "LiveProgressCursor_stageId_fkey"; ALTER TABLE "LiveProgressCursor" DROP CONSTRAINT IF EXISTS "LiveProgressCursor_stageId_fkey";
DROP INDEX IF EXISTS "LiveProgressCursor_stageId_key"; DROP INDEX IF EXISTS "LiveProgressCursor_stageId_key";
DO $$ BEGIN ALTER TABLE "LiveProgressCursor" RENAME COLUMN "stageId" TO "roundId";
ALTER TABLE "LiveProgressCursor" RENAME COLUMN "stageId" TO "roundId";
EXCEPTION WHEN undefined_column THEN NULL; END $$;
CREATE UNIQUE INDEX IF NOT EXISTS "LiveProgressCursor_roundId_key" ON "LiveProgressCursor"("roundId"); CREATE UNIQUE INDEX "LiveProgressCursor_roundId_key" ON "LiveProgressCursor"("roundId");
DO $$ BEGIN ALTER TABLE "LiveProgressCursor" ADD CONSTRAINT "LiveProgressCursor_roundId_fkey"
ALTER TABLE "LiveProgressCursor" ADD CONSTRAINT "LiveProgressCursor_roundId_fkey"
FOREIGN KEY ("roundId") REFERENCES "Round"("id") ON DELETE CASCADE ON UPDATE CASCADE; FOREIGN KEY ("roundId") REFERENCES "Round"("id") ON DELETE CASCADE ON UPDATE CASCADE;
EXCEPTION WHEN duplicate_object THEN NULL; END $$;
-- --- 16. SpecialAward: Drop trackId column --- -- ─── 16. SpecialAward: Drop trackId column ───────────────────────────────────
ALTER TABLE "SpecialAward" DROP CONSTRAINT IF EXISTS "SpecialAward_trackId_fkey"; ALTER TABLE "SpecialAward" DROP CONSTRAINT IF EXISTS "SpecialAward_trackId_fkey";
@ -320,16 +260,12 @@ DROP INDEX IF EXISTS "SpecialAward_trackId_key";
ALTER TABLE "SpecialAward" DROP COLUMN IF EXISTS "trackId"; ALTER TABLE "SpecialAward" DROP COLUMN IF EXISTS "trackId";
-- --- 17. ConflictOfInterest: roundId was made nullable in pipeline migration --- -- ─── 17. ConflictOfInterest: roundId was made nullable in pipeline migration ─
-- It still exists, just restore FK to new Round table -- It still exists, just restore FK to new Round table
DO $$ BEGIN ALTER TABLE "ConflictOfInterest" ADD CONSTRAINT "ConflictOfInterest_roundId_fkey"
ALTER TABLE "ConflictOfInterest" ADD CONSTRAINT "ConflictOfInterest_roundId_fkey"
FOREIGN KEY ("roundId") REFERENCES "Round"("id") ON DELETE CASCADE ON UPDATE CASCADE; FOREIGN KEY ("roundId") REFERENCES "Round"("id") ON DELETE CASCADE ON UPDATE CASCADE;
EXCEPTION WHEN duplicate_object THEN NULL; END $$;
-- --- 18. TaggingJob: roundId was made nullable in pipeline migration --- -- ─── 18. TaggingJob: roundId was made nullable in pipeline migration ─────────
-- Restore FK to new Round table -- Restore FK to new Round table
DO $$ BEGIN ALTER TABLE "TaggingJob" ADD CONSTRAINT "TaggingJob_roundId_fkey"
ALTER TABLE "TaggingJob" ADD CONSTRAINT "TaggingJob_roundId_fkey"
FOREIGN KEY ("roundId") REFERENCES "Round"("id") ON DELETE SET NULL ON UPDATE CASCADE; FOREIGN KEY ("roundId") REFERENCES "Round"("id") ON DELETE SET NULL ON UPDATE CASCADE;
EXCEPTION WHEN duplicate_object THEN NULL; END $$;

View File

@ -1,2 +0,0 @@
-- Add pageCount column to ProjectFile (was in schema but missing migration)
ALTER TABLE "ProjectFile" ADD COLUMN IF NOT EXISTS "pageCount" INTEGER;

View File

@ -1,19 +0,0 @@
-- =============================================================================
-- Schema Reconciliation: Fill remaining gaps between migrations and schema.prisma
-- =============================================================================
-- All statements are idempotent (safe to re-run on any database state).
-- 1. ConflictOfInterest: add standalone hasConflict index (schema has @@index([hasConflict]))
-- Migration 20260205223133 only created composite (roundId, hasConflict) index.
CREATE INDEX IF NOT EXISTS "ConflictOfInterest_hasConflict_idx" ON "ConflictOfInterest"("hasConflict");
-- 2. Ensure ConflictOfInterest.roundId is nullable (schema says String?)
-- Pipeline migration (20260213) makes it nullable, but guard for safety.
DO $$ BEGIN
ALTER TABLE "ConflictOfInterest" ALTER COLUMN "roundId" DROP NOT NULL;
EXCEPTION WHEN others THEN NULL;
END $$;
-- 3. Drop stale composite index that no longer matches schema
-- Schema only has @@index([hasConflict]) and @@index([userId]), not (roundId, hasConflict).
DROP INDEX IF EXISTS "ConflictOfInterest_roundId_hasConflict_idx";

View File

@ -1,6 +1,6 @@
'use client' 'use client'
import { useState, useCallback, useRef, useEffect, useMemo } from 'react' import { useState, useCallback, useRef } from 'react'
import Link from 'next/link' import Link from 'next/link'
import { trpc } from '@/lib/trpc/client' import { trpc } from '@/lib/trpc/client'
import { toast } from 'sonner' import { toast } from 'sonner'
@ -46,8 +46,6 @@ import {
Loader2, Loader2,
FileUp, FileUp,
AlertCircle, AlertCircle,
ExternalLink,
Trash2,
} from 'lucide-react' } from 'lucide-react'
import { cn, formatFileSize } from '@/lib/utils' import { cn, formatFileSize } from '@/lib/utils'
import { Pagination } from '@/components/shared/pagination' import { Pagination } from '@/components/shared/pagination'
@ -79,13 +77,12 @@ export default function BulkUploadPage() {
label: string label: string
mimeTypes: string[] mimeTypes: string[]
required: boolean required: boolean
file: { id: string; fileName: string; bucket: string; objectKey: string } | null file: { id: string; fileName: string } | null
}> }>
} | null>(null) } | null>(null)
const [bulkFiles, setBulkFiles] = useState<Record<string, File | null>>({}) const [bulkFiles, setBulkFiles] = useState<Record<string, File | null>>({})
const fileInputRefs = useRef<Record<string, HTMLInputElement | null>>({}) const fileInputRefs = useRef<Record<string, HTMLInputElement | null>>({})
const utils = trpc.useUtils()
// Debounce search // Debounce search
const searchTimer = useRef<ReturnType<typeof setTimeout>>(undefined) const searchTimer = useRef<ReturnType<typeof setTimeout>>(undefined)
@ -112,70 +109,6 @@ export default function BulkUploadPage() {
{ enabled: !!roundId } { enabled: !!roundId }
) )
// Collect all files from current data for existence verification
const filesToVerify = useMemo(() => {
if (!data?.projects) return []
const files: { bucket: string; objectKey: string }[] = []
for (const row of data.projects) {
for (const req of row.requirements) {
if (req.file?.bucket && req.file?.objectKey) {
files.push({ bucket: req.file.bucket, objectKey: req.file.objectKey })
}
}
}
return files
}, [data])
// Verify files actually exist in storage
const { data: fileExistence } = trpc.file.verifyFilesExist.useQuery(
{ files: filesToVerify },
{ enabled: filesToVerify.length > 0, staleTime: 30_000 }
)
// Track which files are missing from storage (objectKey → true means missing)
const missingFiles = useMemo(() => {
if (!fileExistence) return new Set<string>()
const missing = new Set<string>()
for (const [key, exists] of Object.entries(fileExistence)) {
if (!exists) missing.add(key)
}
return missing
}, [fileExistence])
// Open file in new tab via presigned URL
const handleViewFile = useCallback(
async (bucket: string, objectKey: string) => {
try {
const { url } = await utils.file.getDownloadUrl.fetch({ bucket, objectKey })
window.open(url, '_blank')
} catch {
toast.error('Failed to open file. It may have been deleted from storage.')
refetch()
}
},
[utils, refetch]
)
// Delete a file
const deleteMutation = trpc.file.delete.useMutation({
onSuccess: () => {
toast.success('File removed')
refetch()
},
onError: (err) => {
toast.error(`Failed to remove file: ${err.message}`)
},
})
const handleDeleteFile = useCallback(
(fileId: string) => {
if (confirm('Remove this uploaded file?')) {
deleteMutation.mutate({ id: fileId })
}
},
[deleteMutation]
)
const uploadMutation = trpc.file.adminUploadForRoundRequirement.useMutation() const uploadMutation = trpc.file.adminUploadForRoundRequirement.useMutation()
// Upload a single file for a project requirement // Upload a single file for a project requirement
@ -457,7 +390,7 @@ export default function BulkUploadPage() {
<TableBody> <TableBody>
{data.projects.map((row) => { {data.projects.map((row) => {
const missingRequired = row.requirements.filter( const missingRequired = row.requirements.filter(
(r) => r.required && (!r.file || (r.file?.objectKey && missingFiles.has(r.file.objectKey))) (r) => r.required && !r.file
) )
return ( return (
<TableRow <TableRow
@ -513,57 +446,12 @@ export default function BulkUploadPage() {
Retry Retry
</Button> </Button>
</div> </div>
) : req.file && req.file.objectKey && missingFiles.has(req.file.objectKey) ? (
<div className="flex flex-col items-center gap-1">
<AlertCircle className="h-4 w-4 text-amber-500" />
<span className="text-[10px] text-amber-600 font-medium">Missing</span>
<Button
variant="outline"
size="sm"
className="h-6 px-2 text-[10px]"
onClick={() =>
handleCellUpload(
row.project.id,
req.requirementId,
req.mimeTypes
)
}
>
Re-upload
</Button>
</div>
) : req.file || uploadState?.status === 'complete' ? ( ) : req.file || uploadState?.status === 'complete' ? (
<div className="flex flex-col items-center gap-1"> <div className="flex flex-col items-center gap-1">
<div className="flex items-center gap-1">
<CheckCircle2 className="h-4 w-4 text-green-600" /> <CheckCircle2 className="h-4 w-4 text-green-600" />
{req.file && (
<button
type="button"
className="text-muted-foreground hover:text-destructive transition-colors cursor-pointer"
title="Remove file"
onClick={() => handleDeleteFile(req.file!.id)}
disabled={deleteMutation.isPending}
>
<Trash2 className="h-3 w-3" />
</button>
)}
</div>
{req.file?.bucket && req.file?.objectKey ? (
<button
type="button"
className="text-[10px] text-teal-600 hover:text-teal-800 hover:underline truncate max-w-[120px] flex items-center gap-0.5 cursor-pointer"
onClick={() =>
handleViewFile(req.file!.bucket, req.file!.objectKey)
}
>
{req.file.fileName}
<ExternalLink className="h-2.5 w-2.5 shrink-0" />
</button>
) : (
<span className="text-[10px] text-muted-foreground truncate max-w-[120px]"> <span className="text-[10px] text-muted-foreground truncate max-w-[120px]">
{req.file?.fileName ?? 'Uploaded'} {req.file?.fileName ?? 'Uploaded'}
</span> </span>
)}
</div> </div>
) : ( ) : (
<Button <Button

View File

@ -366,9 +366,8 @@ export default function ProjectsPage() {
} }
const handleCloseTaggingDialog = () => { const handleCloseTaggingDialog = () => {
setAiTagDialogOpen(false)
// Only reset job state if not in progress (preserve polling for background jobs)
if (!taggingInProgress) { if (!taggingInProgress) {
setAiTagDialogOpen(false)
setActiveTaggingJobId(null) setActiveTaggingJobId(null)
setSelectedRoundForTagging('') setSelectedRoundForTagging('')
setSelectedProgramForTagging('') setSelectedProgramForTagging('')
@ -619,22 +618,9 @@ export default function ProjectsPage() {
</p> </p>
</div> </div>
<div className="flex flex-wrap gap-2"> <div className="flex flex-wrap gap-2">
<Button <Button variant="outline" onClick={() => setAiTagDialogOpen(true)}>
variant="outline"
onClick={() => setAiTagDialogOpen(true)}
className={taggingInProgress ? 'border-amber-400 bg-amber-50 dark:bg-amber-950/20' : ''}
>
{taggingInProgress ? (
<Loader2 className="mr-2 h-4 w-4 animate-spin text-amber-600" />
) : (
<Bot className="mr-2 h-4 w-4" /> <Bot className="mr-2 h-4 w-4" />
)}
AI Tags AI Tags
{taggingInProgress && (
<span className="ml-1.5 text-[10px] text-amber-600 font-medium">
{taggingProgressPercent}%
</span>
)}
</Button> </Button>
<Button variant="outline" asChild> <Button variant="outline" asChild>
<Link href="/admin/projects/pool"> <Link href="/admin/projects/pool">
@ -1847,8 +1833,9 @@ export default function ProjectsPage() {
<Button <Button
variant="outline" variant="outline"
onClick={handleCloseTaggingDialog} onClick={handleCloseTaggingDialog}
disabled={taggingInProgress}
> >
{taggingInProgress ? 'Run in Background' : 'Cancel'} Cancel
</Button> </Button>
<Button <Button
onClick={handleStartTagging} onClick={handleStartTagging}

View File

@ -88,7 +88,6 @@ import { CoverageReport } from '@/components/admin/assignment/coverage-report'
import { AssignmentPreviewSheet } from '@/components/admin/assignment/assignment-preview-sheet' import { AssignmentPreviewSheet } from '@/components/admin/assignment/assignment-preview-sheet'
import { CsvExportDialog } from '@/components/shared/csv-export-dialog' import { CsvExportDialog } from '@/components/shared/csv-export-dialog'
import { AnimatedCard } from '@/components/shared/animated-container' import { AnimatedCard } from '@/components/shared/animated-container'
import { AddMemberDialog } from '@/components/admin/jury/add-member-dialog'
import { motion } from 'motion/react' import { motion } from 'motion/react'
// ── Status & type config maps ────────────────────────────────────────────── // ── Status & type config maps ──────────────────────────────────────────────
@ -157,9 +156,6 @@ export default function RoundDetailPage() {
BUSINESS_CONCEPT: Array<{ projectId: string; rank: number; score: number; category: string; strengths: string[]; concerns: string[]; recommendation: string }> BUSINESS_CONCEPT: Array<{ projectId: string; rank: number; score: number; category: string; strengths: string[]; concerns: string[]; recommendation: string }>
} | null>(null) } | null>(null)
const [shortlistDialogOpen, setShortlistDialogOpen] = useState(false) const [shortlistDialogOpen, setShortlistDialogOpen] = useState(false)
const [createJuryOpen, setCreateJuryOpen] = useState(false)
const [newJuryName, setNewJuryName] = useState('')
const [addMemberOpen, setAddMemberOpen] = useState(false)
const utils = trpc.useUtils() const utils = trpc.useUtils()
@ -253,48 +249,11 @@ export default function RoundDetailPage() {
const assignJuryMutation = trpc.round.update.useMutation({ const assignJuryMutation = trpc.round.update.useMutation({
onSuccess: () => { onSuccess: () => {
utils.round.getById.invalidate({ id: roundId }) utils.round.getById.invalidate({ id: roundId })
utils.juryGroup.list.invalidate({ competitionId })
toast.success('Jury group updated') toast.success('Jury group updated')
}, },
onError: (err) => toast.error(err.message), onError: (err) => toast.error(err.message),
}) })
// Jury group detail query (for the assigned group)
const juryGroupId = round?.juryGroupId ?? ''
const { data: juryGroupDetail } = trpc.juryGroup.getById.useQuery(
{ id: juryGroupId },
{ enabled: !!juryGroupId, refetchInterval: 10_000 },
)
const createJuryMutation = trpc.juryGroup.create.useMutation({
onSuccess: (newGroup) => {
utils.juryGroup.list.invalidate({ competitionId })
// Auto-assign the new jury group to this round
assignJuryMutation.mutate({ id: roundId, juryGroupId: newGroup.id })
toast.success(`Jury "${newGroup.name}" created and assigned`)
setCreateJuryOpen(false)
setNewJuryName('')
},
onError: (err) => toast.error(err.message),
})
const deleteJuryMutation = trpc.juryGroup.delete.useMutation({
onSuccess: (result) => {
utils.juryGroup.list.invalidate({ competitionId })
utils.round.getById.invalidate({ id: roundId })
toast.success(`Jury "${result.name}" deleted`)
},
onError: (err) => toast.error(err.message),
})
const removeJuryMemberMutation = trpc.juryGroup.removeMember.useMutation({
onSuccess: () => {
if (juryGroupId) utils.juryGroup.getById.invalidate({ id: juryGroupId })
toast.success('Member removed')
},
onError: (err) => toast.error(err.message),
})
const advanceMutation = trpc.round.advanceProjects.useMutation({ const advanceMutation = trpc.round.advanceProjects.useMutation({
onSuccess: (data) => { onSuccess: (data) => {
utils.round.getById.invalidate({ id: roundId }) utils.round.getById.invalidate({ id: roundId })
@ -543,7 +502,7 @@ export default function RoundDetailPage() {
</Button> </Button>
)} )}
<Link href={poolLink}> <Link href={poolLink}>
<Button variant="outline" size="sm" className="border-white/40 bg-white/15 text-white hover:bg-white/30 hover:text-white"> <Button variant="outline" size="sm" className="border-white/30 text-white hover:bg-white/10 hover:text-white">
<Layers className="h-4 w-4 mr-1.5" /> <Layers className="h-4 w-4 mr-1.5" />
Project Pool Project Pool
</Button> </Button>
@ -694,7 +653,6 @@ export default function RoundDetailPage() {
{ value: 'projects', label: 'Projects', icon: Layers }, { value: 'projects', label: 'Projects', icon: Layers },
...(isFiltering ? [{ value: 'filtering', label: 'Filtering', icon: Shield }] : []), ...(isFiltering ? [{ value: 'filtering', label: 'Filtering', icon: Shield }] : []),
...(isEvaluation ? [{ value: 'assignments', label: 'Assignments', icon: ClipboardList }] : []), ...(isEvaluation ? [{ value: 'assignments', label: 'Assignments', icon: ClipboardList }] : []),
{ value: 'jury', label: 'Jury', icon: Users },
{ value: 'config', label: 'Config', icon: Settings }, { value: 'config', label: 'Config', icon: Settings },
{ value: 'windows', label: 'Submissions', icon: Clock }, { value: 'windows', label: 'Submissions', icon: Clock },
{ value: 'awards', label: 'Awards', icon: Trophy }, { value: 'awards', label: 'Awards', icon: Trophy },
@ -1170,248 +1128,6 @@ export default function RoundDetailPage() {
</TabsContent> </TabsContent>
)} )}
{/* ═══════════ JURY TAB ═══════════ */}
<TabsContent value="jury" className="space-y-6">
{/* Jury Group Selector + Create */}
<Card>
<CardHeader>
<div className="flex items-center justify-between">
<div>
<CardTitle className="text-base">Jury Group</CardTitle>
<CardDescription>
Select or create a jury group for this round
</CardDescription>
</div>
<div className="flex items-center gap-2">
<Button size="sm" variant="outline" onClick={() => setCreateJuryOpen(true)}>
<Plus className="h-4 w-4 mr-1.5" />
New Jury
</Button>
</div>
</div>
</CardHeader>
<CardContent>
{juryGroups && juryGroups.length > 0 ? (
<div className="space-y-4">
<Select
value={round.juryGroupId ?? '__none__'}
onValueChange={(value) => {
assignJuryMutation.mutate({
id: roundId,
juryGroupId: value === '__none__' ? null : value,
})
}}
disabled={assignJuryMutation.isPending}
>
<SelectTrigger className="w-full sm:w-80">
<SelectValue placeholder="Select jury group..." />
</SelectTrigger>
<SelectContent>
<SelectItem value="__none__">No jury assigned</SelectItem>
{juryGroups.map((jg: any) => (
<SelectItem key={jg.id} value={jg.id}>
{jg.name} ({jg._count?.members ?? 0} members)
</SelectItem>
))}
</SelectContent>
</Select>
{/* Delete button for currently selected jury group */}
{round.juryGroupId && (
<AlertDialog>
<AlertDialogTrigger asChild>
<Button size="sm" variant="ghost" className="text-destructive hover:text-destructive">
<Trash2 className="h-4 w-4 mr-1.5" />
Delete &quot;{juryGroup?.name}&quot;
</Button>
</AlertDialogTrigger>
<AlertDialogContent>
<AlertDialogHeader>
<AlertDialogTitle>Delete jury group?</AlertDialogTitle>
<AlertDialogDescription>
This will permanently delete &quot;{juryGroup?.name}&quot; and remove all its members.
Rounds using this jury group will be unlinked. This action cannot be undone.
</AlertDialogDescription>
</AlertDialogHeader>
<AlertDialogFooter>
<AlertDialogCancel>Cancel</AlertDialogCancel>
<AlertDialogAction
onClick={() => deleteJuryMutation.mutate({ id: round.juryGroupId! })}
className="bg-destructive text-destructive-foreground hover:bg-destructive/90"
disabled={deleteJuryMutation.isPending}
>
{deleteJuryMutation.isPending && <Loader2 className="h-4 w-4 mr-1.5 animate-spin" />}
Delete Jury
</AlertDialogAction>
</AlertDialogFooter>
</AlertDialogContent>
</AlertDialog>
)}
</div>
) : (
<div className="flex flex-col items-center justify-center py-10 text-center">
<div className="rounded-full bg-purple-50 p-4 mb-4">
<Users className="h-8 w-8 text-purple-400" />
</div>
<p className="text-sm font-medium">No Jury Groups</p>
<p className="text-xs text-muted-foreground mt-1 max-w-sm">
Create a jury group to assign members who will evaluate projects in this round.
</p>
<Button size="sm" className="mt-4" onClick={() => setCreateJuryOpen(true)}>
<Plus className="h-4 w-4 mr-1.5" />
Create First Jury
</Button>
</div>
)}
</CardContent>
</Card>
{/* Members list (only if a jury group is assigned) */}
{juryGroupDetail && (
<Card>
<CardHeader>
<div className="flex items-center justify-between">
<div>
<CardTitle className="text-base">
Members &mdash; {juryGroupDetail.name}
</CardTitle>
<CardDescription>
{juryGroupDetail.members.length} member{juryGroupDetail.members.length !== 1 ? 's' : ''}
</CardDescription>
</div>
<Button size="sm" onClick={() => setAddMemberOpen(true)}>
<UserPlus className="h-4 w-4 mr-1.5" />
Add Member
</Button>
</div>
</CardHeader>
<CardContent>
{juryGroupDetail.members.length === 0 ? (
<div className="flex flex-col items-center justify-center py-10 text-center">
<div className="rounded-full bg-muted p-4 mb-4">
<UserPlus className="h-8 w-8 text-muted-foreground" />
</div>
<p className="text-sm font-medium">No Members Yet</p>
<p className="text-xs text-muted-foreground mt-1">
Add jury members to start assigning projects for evaluation.
</p>
<Button size="sm" variant="outline" className="mt-4" onClick={() => setAddMemberOpen(true)}>
<UserPlus className="h-4 w-4 mr-1.5" />
Add First Member
</Button>
</div>
) : (
<div className="space-y-1">
{juryGroupDetail.members.map((member: any, idx: number) => (
<div
key={member.id}
className={cn(
'flex items-center justify-between py-2.5 px-3 rounded-md transition-colors',
idx % 2 === 1 && 'bg-muted/30',
)}
>
<div className="min-w-0 flex-1">
<p className="text-sm font-medium truncate">
{member.user.name || 'Unnamed User'}
</p>
<p className="text-xs text-muted-foreground truncate">{member.user.email}</p>
</div>
<AlertDialog>
<AlertDialogTrigger asChild>
<Button
variant="ghost"
size="icon"
className="h-7 w-7 text-destructive hover:text-destructive shrink-0"
>
<Trash2 className="h-3.5 w-3.5" />
</Button>
</AlertDialogTrigger>
<AlertDialogContent>
<AlertDialogHeader>
<AlertDialogTitle>Remove member?</AlertDialogTitle>
<AlertDialogDescription>
Remove {member.user.name || member.user.email} from {juryGroupDetail.name}?
</AlertDialogDescription>
</AlertDialogHeader>
<AlertDialogFooter>
<AlertDialogCancel>Cancel</AlertDialogCancel>
<AlertDialogAction
onClick={() => removeJuryMemberMutation.mutate({ id: member.id })}
className="bg-destructive text-destructive-foreground hover:bg-destructive/90"
>
Remove
</AlertDialogAction>
</AlertDialogFooter>
</AlertDialogContent>
</AlertDialog>
</div>
))}
</div>
)}
</CardContent>
</Card>
)}
{/* Create Jury Dialog */}
<Dialog open={createJuryOpen} onOpenChange={setCreateJuryOpen}>
<DialogContent>
<DialogHeader>
<DialogTitle>Create Jury Group</DialogTitle>
<DialogDescription>
Create a new jury group for this competition. It will be automatically assigned to this round.
</DialogDescription>
</DialogHeader>
<div className="space-y-4">
<div className="space-y-2">
<label className="text-sm font-medium">Name</label>
<Input
placeholder="e.g. Round 1 Jury, Expert Panel, Final Jury"
value={newJuryName}
onChange={(e) => setNewJuryName(e.target.value)}
onKeyDown={(e) => {
if (e.key === 'Enter' && newJuryName.trim()) {
createJuryMutation.mutate({
competitionId,
name: newJuryName.trim(),
slug: newJuryName.trim().toLowerCase().replace(/[^a-z0-9]+/g, '-').replace(/(^-|-$)/g, ''),
})
}
}}
/>
</div>
</div>
<DialogFooter>
<Button variant="outline" onClick={() => setCreateJuryOpen(false)}>Cancel</Button>
<Button
onClick={() => {
createJuryMutation.mutate({
competitionId,
name: newJuryName.trim(),
slug: newJuryName.trim().toLowerCase().replace(/[^a-z0-9]+/g, '-').replace(/(^-|-$)/g, ''),
})
}}
disabled={createJuryMutation.isPending || !newJuryName.trim()}
>
{createJuryMutation.isPending && <Loader2 className="h-4 w-4 mr-1.5 animate-spin" />}
Create
</Button>
</DialogFooter>
</DialogContent>
</Dialog>
{/* Add Member Dialog */}
{juryGroupId && (
<AddMemberDialog
juryGroupId={juryGroupId}
open={addMemberOpen}
onOpenChange={(open) => {
setAddMemberOpen(open)
if (!open) utils.juryGroup.getById.invalidate({ id: juryGroupId })
}}
/>
)}
</TabsContent>
{/* ═══════════ ASSIGNMENTS TAB (Evaluation rounds) ═══════════ */} {/* ═══════════ ASSIGNMENTS TAB (Evaluation rounds) ═══════════ */}
{isEvaluation && ( {isEvaluation && (
<TabsContent value="assignments" className="space-y-6"> <TabsContent value="assignments" className="space-y-6">

View File

@ -36,12 +36,14 @@ export function AddMemberDialog({ juryGroupId, open, onOpenChange }: AddMemberDi
// Search existing user state // Search existing user state
const [searchQuery, setSearchQuery] = useState('') const [searchQuery, setSearchQuery] = useState('')
const [selectedUserId, setSelectedUserId] = useState<string>('') const [selectedUserId, setSelectedUserId] = useState<string>('')
const [role, setRole] = useState<'CHAIR' | 'MEMBER' | 'OBSERVER'>('MEMBER')
const [maxAssignments, setMaxAssignments] = useState<string>('') const [maxAssignments, setMaxAssignments] = useState<string>('')
const [capMode, setCapMode] = useState<string>('') const [capMode, setCapMode] = useState<string>('')
// Invite new user state // Invite new user state
const [inviteName, setInviteName] = useState('') const [inviteName, setInviteName] = useState('')
const [inviteEmail, setInviteEmail] = useState('') const [inviteEmail, setInviteEmail] = useState('')
const [inviteRole, setInviteRole] = useState<'CHAIR' | 'MEMBER' | 'OBSERVER'>('MEMBER')
const [inviteMaxAssignments, setInviteMaxAssignments] = useState<string>('') const [inviteMaxAssignments, setInviteMaxAssignments] = useState<string>('')
const [inviteCapMode, setInviteCapMode] = useState<string>('') const [inviteCapMode, setInviteCapMode] = useState<string>('')
const [inviteExpertise, setInviteExpertise] = useState('') const [inviteExpertise, setInviteExpertise] = useState('')
@ -73,7 +75,7 @@ export function AddMemberDialog({ juryGroupId, open, onOpenChange }: AddMemberDi
addMember({ addMember({
juryGroupId, juryGroupId,
userId: newUser.id, userId: newUser.id,
role: 'MEMBER', role: inviteRole,
maxAssignmentsOverride: inviteMaxAssignments ? parseInt(inviteMaxAssignments, 10) : null, maxAssignmentsOverride: inviteMaxAssignments ? parseInt(inviteMaxAssignments, 10) : null,
capModeOverride: inviteCapMode && inviteCapMode !== 'DEFAULT' ? (inviteCapMode as 'HARD' | 'SOFT' | 'NONE') : null, capModeOverride: inviteCapMode && inviteCapMode !== 'DEFAULT' ? (inviteCapMode as 'HARD' | 'SOFT' | 'NONE') : null,
}) })
@ -98,10 +100,12 @@ export function AddMemberDialog({ juryGroupId, open, onOpenChange }: AddMemberDi
const resetForm = () => { const resetForm = () => {
setSearchQuery('') setSearchQuery('')
setSelectedUserId('') setSelectedUserId('')
setRole('MEMBER')
setMaxAssignments('') setMaxAssignments('')
setCapMode('') setCapMode('')
setInviteName('') setInviteName('')
setInviteEmail('') setInviteEmail('')
setInviteRole('MEMBER')
setInviteMaxAssignments('') setInviteMaxAssignments('')
setInviteCapMode('') setInviteCapMode('')
setInviteExpertise('') setInviteExpertise('')
@ -118,7 +122,7 @@ export function AddMemberDialog({ juryGroupId, open, onOpenChange }: AddMemberDi
addMember({ addMember({
juryGroupId, juryGroupId,
userId: selectedUserId, userId: selectedUserId,
role: 'MEMBER', role,
maxAssignmentsOverride: maxAssignments ? parseInt(maxAssignments, 10) : null, maxAssignmentsOverride: maxAssignments ? parseInt(maxAssignments, 10) : null,
capModeOverride: capMode && capMode !== 'DEFAULT' ? (capMode as 'HARD' | 'SOFT' | 'NONE') : null, capModeOverride: capMode && capMode !== 'DEFAULT' ? (capMode as 'HARD' | 'SOFT' | 'NONE') : null,
}) })
@ -211,6 +215,20 @@ export function AddMemberDialog({ juryGroupId, open, onOpenChange }: AddMemberDi
</div> </div>
<div className="grid grid-cols-2 gap-3"> <div className="grid grid-cols-2 gap-3">
<div className="space-y-2">
<Label htmlFor="role">Group Role</Label>
<Select value={role} onValueChange={(val) => setRole(val as typeof role)}>
<SelectTrigger id="role">
<SelectValue />
</SelectTrigger>
<SelectContent>
<SelectItem value="MEMBER">Member</SelectItem>
<SelectItem value="CHAIR">Chair</SelectItem>
<SelectItem value="OBSERVER">Observer</SelectItem>
</SelectContent>
</Select>
</div>
<div className="space-y-2"> <div className="space-y-2">
<Label htmlFor="capMode">Cap Mode</Label> <Label htmlFor="capMode">Cap Mode</Label>
<Select value={capMode || 'DEFAULT'} onValueChange={setCapMode}> <Select value={capMode || 'DEFAULT'} onValueChange={setCapMode}>
@ -280,6 +298,20 @@ export function AddMemberDialog({ juryGroupId, open, onOpenChange }: AddMemberDi
</div> </div>
<div className="grid grid-cols-2 gap-3"> <div className="grid grid-cols-2 gap-3">
<div className="space-y-2">
<Label htmlFor="inviteGroupRole">Group Role</Label>
<Select value={inviteRole} onValueChange={(val) => setInviteRole(val as typeof inviteRole)}>
<SelectTrigger id="inviteGroupRole">
<SelectValue />
</SelectTrigger>
<SelectContent>
<SelectItem value="MEMBER">Member</SelectItem>
<SelectItem value="CHAIR">Chair</SelectItem>
<SelectItem value="OBSERVER">Observer</SelectItem>
</SelectContent>
</Select>
</div>
<div className="space-y-2"> <div className="space-y-2">
<Label htmlFor="inviteCapMode">Cap Mode</Label> <Label htmlFor="inviteCapMode">Cap Mode</Label>
<Select value={inviteCapMode || 'DEFAULT'} onValueChange={setInviteCapMode}> <Select value={inviteCapMode || 'DEFAULT'} onValueChange={setInviteCapMode}>

View File

@ -29,15 +29,15 @@ import { AddMemberDialog } from './add-member-dialog'
interface JuryMember { interface JuryMember {
id: string id: string
userId: string userId: string
role?: string role: string
user: { user: {
id: string id: string
name: string | null name: string | null
email: string email: string
} }
maxAssignmentsOverride?: number | null maxAssignmentsOverride: number | null
capModeOverride?: string | null capModeOverride: string | null
preferredStartupRatio?: number | null preferredStartupRatio: number | null
} }
interface JuryMembersTableProps { interface JuryMembersTableProps {
@ -81,6 +81,7 @@ export function JuryMembersTable({ juryGroupId, members }: JuryMembersTableProps
<TableRow> <TableRow>
<TableHead>Name</TableHead> <TableHead>Name</TableHead>
<TableHead>Email</TableHead> <TableHead>Email</TableHead>
<TableHead className="hidden md:table-cell">Role</TableHead>
<TableHead className="hidden sm:table-cell">Max Assignments</TableHead> <TableHead className="hidden sm:table-cell">Max Assignments</TableHead>
<TableHead className="hidden lg:table-cell">Cap Mode</TableHead> <TableHead className="hidden lg:table-cell">Cap Mode</TableHead>
<TableHead>Actions</TableHead> <TableHead>Actions</TableHead>
@ -89,7 +90,7 @@ export function JuryMembersTable({ juryGroupId, members }: JuryMembersTableProps
<TableBody> <TableBody>
{members.length === 0 ? ( {members.length === 0 ? (
<TableRow> <TableRow>
<TableCell colSpan={5} className="text-center text-muted-foreground"> <TableCell colSpan={6} className="text-center text-muted-foreground">
No members yet. Add members to get started. No members yet. Add members to get started.
</TableCell> </TableCell>
</TableRow> </TableRow>
@ -102,6 +103,11 @@ export function JuryMembersTable({ juryGroupId, members }: JuryMembersTableProps
<TableCell className="text-sm text-muted-foreground"> <TableCell className="text-sm text-muted-foreground">
{member.user.email} {member.user.email}
</TableCell> </TableCell>
<TableCell className="hidden md:table-cell">
<Badge variant={member.role === 'CHAIR' ? 'default' : 'secondary'}>
{member.role}
</Badge>
</TableCell>
<TableCell className="hidden sm:table-cell"> <TableCell className="hidden sm:table-cell">
{member.maxAssignmentsOverride ?? '—'} {member.maxAssignmentsOverride ?? '—'}
</TableCell> </TableCell>

View File

@ -1295,8 +1295,6 @@ export const fileRouter = router({
size: true, size: true,
createdAt: true, createdAt: true,
requirementId: true, requirementId: true,
bucket: true,
objectKey: true,
}, },
}, },
}, },
@ -1489,37 +1487,4 @@ export const fileRouter = router({
return { uploadUrl, file } return { uploadUrl, file }
}), }),
/**
* Verify that files actually exist in storage (MinIO/S3).
* Returns a map of objectKey exists boolean.
*/
verifyFilesExist: adminProcedure
.input(
z.object({
files: z.array(
z.object({
bucket: z.string(),
objectKey: z.string(),
})
).max(200),
})
)
.query(async ({ input }) => {
const { getMinioClient } = await import('@/lib/minio')
const client = getMinioClient()
const results: Record<string, boolean> = {}
await Promise.all(
input.files.map(async ({ bucket, objectKey }) => {
try {
await client.statObject(bucket, objectKey)
results[objectKey] = true
} catch {
results[objectKey] = false
}
})
)
return results
}),
}) })

View File

@ -249,50 +249,6 @@ export const juryGroupRouter = router({
return existing return existing
}), }),
/**
* Delete a jury group entirely
*/
delete: adminProcedure
.input(z.object({ id: z.string() }))
.mutation(async ({ ctx, input }) => {
const group = await ctx.prisma.juryGroup.findUniqueOrThrow({
where: { id: input.id },
include: {
_count: { select: { assignments: true, rounds: true } },
},
})
// Unlink any rounds that reference this jury group
await ctx.prisma.round.updateMany({
where: { juryGroupId: input.id },
data: { juryGroupId: null },
})
// Delete all members first (cascade should handle this, but be explicit)
await ctx.prisma.juryGroupMember.deleteMany({
where: { juryGroupId: input.id },
})
await ctx.prisma.juryGroup.delete({ where: { id: input.id } })
await logAudit({
prisma: ctx.prisma,
userId: ctx.user.id,
action: 'DELETE',
entityType: 'JuryGroup',
entityId: input.id,
detailsJson: {
name: group.name,
competitionId: group.competitionId,
memberCount: group._count.assignments,
},
ipAddress: ctx.ip,
userAgent: ctx.userAgent,
})
return { success: true, name: group.name }
}),
/** /**
* Update a jury group member's role/overrides * Update a jury group member's role/overrides
*/ */

View File

@ -5,7 +5,6 @@ import { prisma } from '@/lib/prisma'
import { logAudit } from '../utils/audit' import { logAudit } from '../utils/audit'
import { import {
tagProject, tagProject,
tagProjectsBatch,
getTagSuggestions, getTagSuggestions,
addProjectTag, addProjectTag,
removeProjectTag, removeProjectTag,
@ -18,7 +17,7 @@ import {
NotificationTypes, NotificationTypes,
} from '../services/in-app-notification' } from '../services/in-app-notification'
// Background job runner for tagging — uses batched API calls for efficiency // Background job runner for tagging
async function runTaggingJob(jobId: string, userId: string) { async function runTaggingJob(jobId: string, userId: string) {
const job = await prisma.taggingJob.findUnique({ const job = await prisma.taggingJob.findUnique({
where: { id: jobId }, where: { id: jobId },
@ -29,7 +28,7 @@ async function runTaggingJob(jobId: string, userId: string) {
return return
} }
console.log(`[AI Tagging Job] Starting job ${jobId} (batched mode)...`) console.log(`[AI Tagging Job] Starting job ${jobId}...`)
// Mark as running // Mark as running
await prisma.taggingJob.update({ await prisma.taggingJob.update({
@ -57,7 +56,7 @@ async function runTaggingJob(jobId: string, userId: string) {
const allProjects = await prisma.project.findMany({ const allProjects = await prisma.project.findMany({
where: whereClause, where: whereClause,
select: { id: true, title: true, tags: true, projectTags: { select: { tagId: true } } }, select: { id: true, title: true, tags: true },
}) })
const untaggedProjects = allProjects.filter(p => p.tags.length === 0) const untaggedProjects = allProjects.filter(p => p.tags.length === 0)
@ -84,33 +83,48 @@ async function runTaggingJob(jobId: string, userId: string) {
return return
} }
let taggedCount = 0
let failedCount = 0
const errors: string[] = []
const startTime = Date.now() const startTime = Date.now()
// Use batched tagging — processes 10 projects per API call, 3 concurrent calls for (let i = 0; i < untaggedProjects.length; i++) {
const { results, totalTokens } = await tagProjectsBatch( const project = untaggedProjects[i]
untaggedProjects, console.log(`[AI Tagging Job] Processing ${i + 1}/${untaggedProjects.length}: "${project.title.substring(0, 40)}..."`)
userId,
async (processed, total) => { try {
// Update job progress on each batch completion const result = await tagProject(project.id, userId)
const taggedSoFar = results?.length ?? processed taggedCount++
console.log(`[AI Tagging Job] ✓ Tagged with ${result.applied.length} tags`)
} catch (error) {
failedCount++
const errorMsg = error instanceof Error ? error.message : 'Unknown error'
errors.push(`${project.title}: ${errorMsg}`)
console.error(`[AI Tagging Job] ✗ Failed: ${errorMsg}`)
}
// Update progress
await prisma.taggingJob.update({ await prisma.taggingJob.update({
where: { id: jobId }, where: { id: jobId },
data: { data: {
processedCount: processed, processedCount: i + 1,
taggedCount: taggedSoFar, taggedCount,
failedCount,
errorsJson: errors.length > 0 ? errors.slice(0, 20) : undefined, // Keep last 20 errors
}, },
}) })
// Log progress every 10 projects
if ((i + 1) % 10 === 0) {
const elapsed = ((Date.now() - startTime) / 1000).toFixed(0) const elapsed = ((Date.now() - startTime) / 1000).toFixed(0)
console.log(`[AI Tagging Job] Progress: ${processed}/${total} (${elapsed}s elapsed)`) const avgTime = (Date.now() - startTime) / (i + 1) / 1000
const remaining = avgTime * (untaggedProjects.length - i - 1)
console.log(`[AI Tagging Job] Progress: ${i + 1}/${untaggedProjects.length} (${elapsed}s elapsed, ~${remaining.toFixed(0)}s remaining)`)
}
} }
)
const taggedCount = results.filter(r => r.applied.length > 0).length
const failedCount = untaggedProjects.length - results.length
const totalTime = ((Date.now() - startTime) / 1000).toFixed(1) const totalTime = ((Date.now() - startTime) / 1000).toFixed(1)
console.log(`[AI Tagging Job] Complete: ${taggedCount} tagged, ${failedCount} failed in ${totalTime}s (${totalTokens} tokens)`) console.log(`[AI Tagging Job] Complete: ${taggedCount} tagged, ${failedCount} failed in ${totalTime}s`)
// Mark as completed // Mark as completed
await prisma.taggingJob.update({ await prisma.taggingJob.update({
@ -118,9 +132,7 @@ async function runTaggingJob(jobId: string, userId: string) {
data: { data: {
status: 'COMPLETED', status: 'COMPLETED',
completedAt: new Date(), completedAt: new Date(),
processedCount: results.length, errorsJson: errors.length > 0 ? errors : undefined,
taggedCount,
failedCount,
}, },
}) })
@ -132,7 +144,7 @@ async function runTaggingJob(jobId: string, userId: string) {
linkUrl: '/admin/projects', linkUrl: '/admin/projects',
linkLabel: 'View Projects', linkLabel: 'View Projects',
priority: 'normal', priority: 'normal',
metadata: { jobId, taggedCount, failedCount, skippedCount, totalTokens }, metadata: { jobId, taggedCount, failedCount, skippedCount },
}) })
} catch (error) { } catch (error) {

View File

@ -142,7 +142,7 @@ interface FilteringRuleInput {
const DEFAULT_BATCH_SIZE = 20 const DEFAULT_BATCH_SIZE = 20
const MAX_BATCH_SIZE = 50 const MAX_BATCH_SIZE = 50
const MIN_BATCH_SIZE = 1 const MIN_BATCH_SIZE = 1
const DEFAULT_PARALLEL_BATCHES = 3 const DEFAULT_PARALLEL_BATCHES = 1
const MAX_PARALLEL_BATCHES = 10 const MAX_PARALLEL_BATCHES = 10
// Structured system prompt for AI screening // Structured system prompt for AI screening

View File

@ -344,8 +344,8 @@ export async function generateShortlist(
let totalTokens = 0 let totalTokens = 0
const allErrors: string[] = [] const allErrors: string[] = []
// Run categories in parallel for efficiency // Run each category independently
const categoryPromises = categories.map(async (cat) => { for (const cat of categories) {
const catTopN = cat === 'STARTUP' const catTopN = cat === 'STARTUP'
? (startupTopN ?? topN) ? (startupTopN ?? topN)
: (conceptTopN ?? topN) : (conceptTopN ?? topN)
@ -357,12 +357,6 @@ export async function generateShortlist(
prisma, prisma,
) )
return { cat, result }
})
const categoryResults = await Promise.all(categoryPromises)
for (const { cat, result } of categoryResults) {
if (cat === 'STARTUP') { if (cat === 'STARTUP') {
allRecommendations.STARTUP = result.recommendations allRecommendations.STARTUP = result.recommendations
} else { } else {

View File

@ -5,7 +5,7 @@
* *
* Features: * Features:
* - Single project tagging (on-submit or manual) * - Single project tagging (on-submit or manual)
* - Batch tagging with concurrent processing (10 projects/batch, 3 concurrent) * - Batch tagging for rounds
* - Confidence scores for each tag * - Confidence scores for each tag
* - Additive only - never removes existing tags * - Additive only - never removes existing tags
* *
@ -53,10 +53,8 @@ interface AvailableTag {
const CONFIDENCE_THRESHOLD = 0.5 const CONFIDENCE_THRESHOLD = 0.5
const DEFAULT_MAX_TAGS = 5 const DEFAULT_MAX_TAGS = 5
const BATCH_SIZE = 10 // Projects per API call
const BATCH_CONCURRENCY = 3 // Concurrent API calls
// System prompt optimized for single-project tag suggestion // System prompt optimized for tag suggestion
const TAG_SUGGESTION_SYSTEM_PROMPT = `You are an expert at categorizing ocean conservation and sustainability projects. const TAG_SUGGESTION_SYSTEM_PROMPT = `You are an expert at categorizing ocean conservation and sustainability projects.
Analyze the project and suggest the most relevant expertise tags from the provided list. Analyze the project and suggest the most relevant expertise tags from the provided list.
@ -80,36 +78,6 @@ Rules:
- Maximum 7 suggestions per project - Maximum 7 suggestions per project
- Be conservative - only suggest tags that truly apply` - Be conservative - only suggest tags that truly apply`
// System prompt optimized for batch tagging (multiple projects in one call)
const BATCH_TAG_SYSTEM_PROMPT = `You are an expert at categorizing ocean conservation and sustainability projects.
Analyze EACH project and suggest the most relevant expertise tags from the provided list.
Consider each project's focus areas, technology, methodology, and domain.
Return JSON with this format:
{
"projects": [
{
"project_id": "PROJECT_001",
"suggestions": [
{
"tag_name": "exact tag name from list",
"confidence": 0.0-1.0,
"reasoning": "brief explanation"
}
]
}
]
}
Rules:
- Only suggest tags from the provided list (exact names)
- Order by relevance (most relevant first)
- Confidence should reflect how well the tag matches
- Maximum 7 suggestions per project
- Be conservative - only suggest tags that truly apply
- Return results for ALL projects provided`
// ─── Helper Functions ──────────────────────────────────────────────────────── // ─── Helper Functions ────────────────────────────────────────────────────────
/** /**
@ -164,8 +132,7 @@ export async function getAvailableTags(): Promise<AvailableTag[]> {
// ─── AI Tagging Core ───────────────────────────────────────────────────────── // ─── AI Tagging Core ─────────────────────────────────────────────────────────
/** /**
* Call OpenAI to get tag suggestions for a single project * Call OpenAI to get tag suggestions for a project
* Used for on-demand single-project tagging
*/ */
async function getAISuggestions( async function getAISuggestions(
anonymizedProject: AnonymizedProjectForAI, anonymizedProject: AnonymizedProjectForAI,
@ -180,7 +147,7 @@ async function getAISuggestions(
const model = await getConfiguredModel() const model = await getConfiguredModel()
// Build compact tag list for prompt // Build tag list for prompt
const tagList = availableTags.map((t) => ({ const tagList = availableTags.map((t) => ({
name: t.name, name: t.name,
category: t.category, category: t.category,
@ -188,10 +155,10 @@ async function getAISuggestions(
})) }))
const userPrompt = `PROJECT: const userPrompt = `PROJECT:
${JSON.stringify(anonymizedProject)} ${JSON.stringify(anonymizedProject, null, 2)}
AVAILABLE TAGS: AVAILABLE TAGS:
${JSON.stringify(tagList)} ${JSON.stringify(tagList, null, 2)}
Suggest relevant tags for this project.` Suggest relevant tags for this project.`
@ -279,160 +246,6 @@ Suggest relevant tags for this project.`
} }
} }
/**
* Call OpenAI to get tag suggestions for a batch of projects in one API call.
* Returns a map of project_id -> TagSuggestion[].
*/
async function getAISuggestionsBatch(
anonymizedProjects: AnonymizedProjectForAI[],
availableTags: AvailableTag[],
userId?: string
): Promise<{ suggestionsMap: Map<string, TagSuggestion[]>; tokensUsed: number }> {
const openai = await getOpenAI()
if (!openai) {
console.warn('[AI Tagging] OpenAI not configured')
return { suggestionsMap: new Map(), tokensUsed: 0 }
}
const model = await getConfiguredModel()
const suggestionsMap = new Map<string, TagSuggestion[]>()
// Build compact tag list (sent once for entire batch)
const tagList = availableTags.map((t) => ({
name: t.name,
category: t.category,
description: t.description,
}))
const userPrompt = `PROJECTS (${anonymizedProjects.length}):
${JSON.stringify(anonymizedProjects)}
AVAILABLE TAGS:
${JSON.stringify(tagList)}
Suggest relevant tags for each project.`
const MAX_PARSE_RETRIES = 2
let parseAttempts = 0
try {
const params = buildCompletionParams(model, {
messages: [
{ role: 'system', content: BATCH_TAG_SYSTEM_PROMPT },
{ role: 'user', content: userPrompt },
],
jsonMode: true,
temperature: 0.1,
maxTokens: Math.min(4000, anonymizedProjects.length * 500),
})
let response = await openai.chat.completions.create(params)
let usage = extractTokenUsage(response)
let totalTokens = usage.totalTokens
// Parse with retry logic
let parsed: {
projects: Array<{
project_id: string
suggestions: Array<{
tag_name: string
confidence: number
reasoning: string
}>
}>
}
while (true) {
try {
const content = response.choices[0]?.message?.content
if (!content) throw new Error('Empty response from AI')
const raw = JSON.parse(content)
parsed = raw.projects ? raw : { projects: Array.isArray(raw) ? raw : [] }
break
} catch (parseError) {
if (parseError instanceof SyntaxError && parseAttempts < MAX_PARSE_RETRIES) {
parseAttempts++
console.warn(`[AI Tagging Batch] JSON parse failed, retrying (${parseAttempts}/${MAX_PARSE_RETRIES})`)
const retryParams = buildCompletionParams(model, {
messages: [
{ role: 'system', content: BATCH_TAG_SYSTEM_PROMPT },
{ role: 'user', content: userPrompt + '\n\nIMPORTANT: Please ensure valid JSON output.' },
],
jsonMode: true,
temperature: 0.1,
maxTokens: Math.min(4000, anonymizedProjects.length * 500),
})
response = await openai.chat.completions.create(retryParams)
const retryUsage = extractTokenUsage(response)
totalTokens += retryUsage.totalTokens
continue
}
throw parseError
}
}
// Log usage for the entire batch
await logAIUsage({
userId,
action: 'PROJECT_TAGGING',
entityType: 'Project',
model,
promptTokens: usage.promptTokens,
completionTokens: usage.completionTokens,
totalTokens,
batchSize: anonymizedProjects.length,
itemsProcessed: parsed.projects?.length || 0,
status: 'SUCCESS',
})
// Map results back to TagSuggestion format
for (const projectResult of parsed.projects || []) {
const suggestions: TagSuggestion[] = []
for (const s of projectResult.suggestions || []) {
const tag = availableTags.find(
(t) => t.name.toLowerCase() === s.tag_name.toLowerCase()
)
if (tag) {
suggestions.push({
tagId: tag.id,
tagName: tag.name,
confidence: Math.max(0, Math.min(1, s.confidence)),
reasoning: s.reasoning || '',
})
}
}
suggestionsMap.set(projectResult.project_id, suggestions)
}
return { suggestionsMap, tokensUsed: totalTokens }
} catch (error) {
if (error instanceof SyntaxError) {
const parseError = createParseError(error.message)
logAIError('Tagging', 'getAISuggestionsBatch', parseError)
}
const classified = classifyAIError(error)
logAIError('Tagging', 'getAISuggestionsBatch', classified)
await logAIUsage({
userId,
action: 'PROJECT_TAGGING',
entityType: 'Project',
model: 'unknown',
promptTokens: 0,
completionTokens: 0,
totalTokens: 0,
batchSize: anonymizedProjects.length,
itemsProcessed: 0,
status: 'ERROR',
errorMessage: error instanceof Error ? error.message : 'Unknown error',
})
throw error
}
}
// ─── Public API ────────────────────────────────────────────────────────────── // ─── Public API ──────────────────────────────────────────────────────────────
/** /**
@ -542,153 +355,6 @@ export async function tagProject(
} }
} }
/**
* Tag a batch of projects using batched API calls with concurrency.
* Much more efficient than tagging one-by-one for bulk operations.
*
* @param projects Array of { id, projectTags } to tag
* @param userId The user initiating the tagging
* @param onProgress Callback for progress updates
* @returns Array of TaggingResult
*/
export async function tagProjectsBatch(
projects: Array<{
id: string
title: string
projectTags: Array<{ tagId: string }>
}>,
userId: string,
onProgress?: (processed: number, total: number) => Promise<void>
): Promise<{ results: TaggingResult[]; totalTokens: number }> {
const settings = await getTaggingSettings()
if (!settings.enabled) {
return { results: [], totalTokens: 0 }
}
const availableTags = await getAvailableTags()
if (availableTags.length === 0) {
return { results: [], totalTokens: 0 }
}
// Fetch full project data for all projects at once (single DB query)
const fullProjects = await prisma.project.findMany({
where: { id: { in: projects.map((p) => p.id) } },
include: {
projectTags: true,
files: { select: { fileType: true } },
_count: { select: { teamMembers: true, files: true } },
},
})
const projectMap = new Map(fullProjects.map((p) => [p.id, p]))
// Anonymize all projects at once
const projectsWithRelations = fullProjects.map(toProjectWithRelations)
const { anonymized, mappings } = anonymizeProjectsForAI(projectsWithRelations, 'FILTERING')
if (!validateAnonymizedProjects(anonymized)) {
throw new Error('GDPR compliance check failed: PII detected in anonymized data')
}
// Build mapping from anonymous ID to real project
const anonToRealMap = new Map<string, string>()
for (const mapping of mappings) {
anonToRealMap.set(mapping.anonymousId, mapping.realId)
}
// Split into batches
const batches: AnonymizedProjectForAI[][] = []
for (let i = 0; i < anonymized.length; i += BATCH_SIZE) {
batches.push(anonymized.slice(i, i + BATCH_SIZE))
}
const allResults: TaggingResult[] = []
let totalTokens = 0
let processedCount = 0
// Process batches with concurrency
for (let i = 0; i < batches.length; i += BATCH_CONCURRENCY) {
const concurrentBatches = batches.slice(i, i + BATCH_CONCURRENCY)
const batchPromises = concurrentBatches.map(async (batch) => {
try {
const { suggestionsMap, tokensUsed } = await getAISuggestionsBatch(
batch,
availableTags,
userId
)
return { suggestionsMap, tokensUsed, error: null }
} catch (error) {
console.error('[AI Tagging Batch] Batch failed:', error)
return { suggestionsMap: new Map<string, TagSuggestion[]>(), tokensUsed: 0, error }
}
})
const batchResults = await Promise.all(batchPromises)
// Process results from all concurrent batches
for (const { suggestionsMap, tokensUsed } of batchResults) {
totalTokens += tokensUsed
for (const [anonId, suggestions] of suggestionsMap) {
const realId = anonToRealMap.get(anonId)
if (!realId) continue
const project = projectMap.get(realId)
if (!project) continue
// Filter by confidence
const validSuggestions = suggestions.filter(
(s) => s.confidence >= settings.confidenceThreshold
)
// Get existing tags
const existingTagIds = new Set(project.projectTags.map((pt) => pt.tagId))
const currentTagCount = project.projectTags.length
const remainingSlots = Math.max(0, settings.maxTags - currentTagCount)
const newSuggestions = validSuggestions
.filter((s) => !existingTagIds.has(s.tagId))
.slice(0, remainingSlots)
// Apply tags
const applied: TagSuggestion[] = []
for (const suggestion of newSuggestions) {
try {
await prisma.projectTag.create({
data: {
projectId: realId,
tagId: suggestion.tagId,
confidence: suggestion.confidence,
source: 'AI',
},
})
applied.push(suggestion)
} catch {
// Skip duplicates
}
}
allResults.push({
projectId: realId,
suggestions,
applied,
tokensUsed: 0, // Token tracking is per-batch, not per-project
})
processedCount++
}
}
// Report progress after each concurrent chunk
if (onProgress) {
await onProgress(processedCount, projects.length)
}
}
return { results: allResults, totalTokens }
}
/** /**
* Get tag suggestions for a project without applying them * Get tag suggestions for a project without applying them
* Useful for preview/review before applying * Useful for preview/review before applying