Bunch of updates to scheduling
This commit is contained in:
@@ -0,0 +1,12 @@
|
||||
-- Add POLL to CalendarItemSource enum
|
||||
ALTER TYPE "CalendarItemSource" ADD VALUE IF NOT EXISTS 'POLL';
|
||||
|
||||
-- AlterTable
|
||||
ALTER TABLE "scheduling_polls" ADD COLUMN "auto_finalize" BOOLEAN NOT NULL DEFAULT false;
|
||||
ALTER TABLE "scheduling_polls" ADD COLUMN "auto_finalize_threshold" INTEGER;
|
||||
ALTER TABLE "scheduling_polls" ADD COLUMN "auto_convert_to_calendar" BOOLEAN NOT NULL DEFAULT false;
|
||||
ALTER TABLE "scheduling_polls" ADD COLUMN "auto_convert_to_gancio" BOOLEAN NOT NULL DEFAULT false;
|
||||
ALTER TABLE "scheduling_polls" ADD COLUMN "auto_convert_to_shift" BOOLEAN NOT NULL DEFAULT false;
|
||||
ALTER TABLE "scheduling_polls" ADD COLUMN "tie_breaker" TEXT NOT NULL DEFAULT 'earliest';
|
||||
ALTER TABLE "scheduling_polls" ADD COLUMN "auto_finalize_job_id" TEXT;
|
||||
ALTER TABLE "scheduling_polls" ADD COLUMN "converted_calendar_item_id" TEXT;
|
||||
@@ -0,0 +1,160 @@
|
||||
-- AlterEnum
|
||||
ALTER TYPE "ContactActivityType" ADD VALUE 'POLL_VOTED';
|
||||
|
||||
-- AlterEnum
|
||||
ALTER TYPE "ContactSource" ADD VALUE 'POLL_VOTE';
|
||||
|
||||
-- AlterEnum
|
||||
ALTER TYPE "SignupSource" ADD VALUE 'POLL_CONVERSION';
|
||||
|
||||
-- AlterTable
|
||||
ALTER TABLE "contacts" ADD COLUMN "pronouns" TEXT;
|
||||
|
||||
-- AlterTable
|
||||
ALTER TABLE "scheduling_poll_votes" ADD COLUMN "contact_id" TEXT;
|
||||
|
||||
-- AlterTable
|
||||
ALTER TABLE "scheduling_polls" ADD COLUMN "auto_enroll_voters" BOOLEAN NOT NULL DEFAULT true;
|
||||
|
||||
-- AlterTable
|
||||
ALTER TABLE "users" ADD COLUMN "pronouns" TEXT;
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "participant_needs" (
|
||||
"id" TEXT NOT NULL,
|
||||
"user_id" TEXT,
|
||||
"contact_id" TEXT,
|
||||
"needs_wheelchair" BOOLEAN NOT NULL DEFAULT false,
|
||||
"needs_ground_floor" BOOLEAN NOT NULL DEFAULT false,
|
||||
"needs_hearing_loop" BOOLEAN NOT NULL DEFAULT false,
|
||||
"needs_sign_language" BOOLEAN NOT NULL DEFAULT false,
|
||||
"other_accessibility" TEXT,
|
||||
"is_vegan" BOOLEAN NOT NULL DEFAULT false,
|
||||
"is_vegetarian" BOOLEAN NOT NULL DEFAULT false,
|
||||
"is_gluten_free" BOOLEAN NOT NULL DEFAULT false,
|
||||
"is_halal" BOOLEAN NOT NULL DEFAULT false,
|
||||
"is_kosher" BOOLEAN NOT NULL DEFAULT false,
|
||||
"has_nut_allergy" BOOLEAN NOT NULL DEFAULT false,
|
||||
"other_dietary" TEXT,
|
||||
"needs_childcare" BOOLEAN NOT NULL DEFAULT false,
|
||||
"childcare_details" TEXT,
|
||||
"needs_transportation" BOOLEAN NOT NULL DEFAULT false,
|
||||
"transportation_notes" TEXT,
|
||||
"preferred_language" TEXT DEFAULT 'en',
|
||||
"needs_translation" BOOLEAN NOT NULL DEFAULT false,
|
||||
"translation_language" TEXT,
|
||||
"visibility_consent" TEXT NOT NULL DEFAULT 'organizer_only',
|
||||
"updated_at" TIMESTAMP(3) NOT NULL,
|
||||
|
||||
CONSTRAINT "participant_needs_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "meeting_agendas" (
|
||||
"id" TEXT NOT NULL,
|
||||
"shift_id" TEXT,
|
||||
"poll_id" TEXT,
|
||||
"title" TEXT NOT NULL,
|
||||
"items" JSONB NOT NULL DEFAULT '[]',
|
||||
"status" TEXT NOT NULL DEFAULT 'draft',
|
||||
"created_by_user_id" TEXT NOT NULL,
|
||||
"created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"updated_at" TIMESTAMP(3) NOT NULL,
|
||||
|
||||
CONSTRAINT "meeting_agendas_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "meeting_minutes" (
|
||||
"id" TEXT NOT NULL,
|
||||
"agenda_id" TEXT NOT NULL,
|
||||
"notes" TEXT NOT NULL,
|
||||
"decisions" JSONB NOT NULL DEFAULT '[]',
|
||||
"attendees" JSONB NOT NULL DEFAULT '[]',
|
||||
"approved_at" TIMESTAMP(3),
|
||||
"approved_by_user_id" TEXT,
|
||||
"created_by_user_id" TEXT NOT NULL,
|
||||
"created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"updated_at" TIMESTAMP(3) NOT NULL,
|
||||
|
||||
CONSTRAINT "meeting_minutes_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "action_items" (
|
||||
"id" TEXT NOT NULL,
|
||||
"agenda_id" TEXT,
|
||||
"title" TEXT NOT NULL,
|
||||
"description" TEXT,
|
||||
"assignee_user_id" TEXT,
|
||||
"due_date" TIMESTAMP(3),
|
||||
"status" TEXT NOT NULL DEFAULT 'open',
|
||||
"priority" TEXT NOT NULL DEFAULT 'normal',
|
||||
"completed_at" TIMESTAMP(3),
|
||||
"created_by_user_id" TEXT NOT NULL,
|
||||
"created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"updated_at" TIMESTAMP(3) NOT NULL,
|
||||
|
||||
CONSTRAINT "action_items_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateIndex
|
||||
CREATE UNIQUE INDEX "participant_needs_user_id_key" ON "participant_needs"("user_id");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE UNIQUE INDEX "participant_needs_contact_id_key" ON "participant_needs"("contact_id");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE UNIQUE INDEX "meeting_agendas_shift_id_key" ON "meeting_agendas"("shift_id");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE UNIQUE INDEX "meeting_agendas_poll_id_key" ON "meeting_agendas"("poll_id");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE UNIQUE INDEX "meeting_minutes_agenda_id_key" ON "meeting_minutes"("agenda_id");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "action_items_assignee_user_id_status_idx" ON "action_items"("assignee_user_id", "status");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "action_items_due_date_idx" ON "action_items"("due_date");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "scheduling_poll_votes_contact_id_idx" ON "scheduling_poll_votes"("contact_id");
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "scheduling_poll_votes" ADD CONSTRAINT "scheduling_poll_votes_contact_id_fkey" FOREIGN KEY ("contact_id") REFERENCES "contacts"("id") ON DELETE SET NULL ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "participant_needs" ADD CONSTRAINT "participant_needs_user_id_fkey" FOREIGN KEY ("user_id") REFERENCES "users"("id") ON DELETE SET NULL ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "participant_needs" ADD CONSTRAINT "participant_needs_contact_id_fkey" FOREIGN KEY ("contact_id") REFERENCES "contacts"("id") ON DELETE SET NULL ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "meeting_agendas" ADD CONSTRAINT "meeting_agendas_shift_id_fkey" FOREIGN KEY ("shift_id") REFERENCES "shifts"("id") ON DELETE SET NULL ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "meeting_agendas" ADD CONSTRAINT "meeting_agendas_poll_id_fkey" FOREIGN KEY ("poll_id") REFERENCES "scheduling_polls"("id") ON DELETE SET NULL ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "meeting_agendas" ADD CONSTRAINT "meeting_agendas_created_by_user_id_fkey" FOREIGN KEY ("created_by_user_id") REFERENCES "users"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "meeting_minutes" ADD CONSTRAINT "meeting_minutes_agenda_id_fkey" FOREIGN KEY ("agenda_id") REFERENCES "meeting_agendas"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "meeting_minutes" ADD CONSTRAINT "meeting_minutes_approved_by_user_id_fkey" FOREIGN KEY ("approved_by_user_id") REFERENCES "users"("id") ON DELETE SET NULL ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "meeting_minutes" ADD CONSTRAINT "meeting_minutes_created_by_user_id_fkey" FOREIGN KEY ("created_by_user_id") REFERENCES "users"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "action_items" ADD CONSTRAINT "action_items_agenda_id_fkey" FOREIGN KEY ("agenda_id") REFERENCES "meeting_agendas"("id") ON DELETE SET NULL ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "action_items" ADD CONSTRAINT "action_items_assignee_user_id_fkey" FOREIGN KEY ("assignee_user_id") REFERENCES "users"("id") ON DELETE SET NULL ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "action_items" ADD CONSTRAINT "action_items_created_by_user_id_fkey" FOREIGN KEY ("created_by_user_id") REFERENCES "users"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
|
||||
|
||||
@@ -48,6 +48,7 @@ model User {
|
||||
password String // bcrypt hashed
|
||||
name String?
|
||||
phone String?
|
||||
pronouns String?
|
||||
role UserRole @default(USER)
|
||||
roles Json @default("[]") // Array of UserRole strings for multi-role support
|
||||
status UserStatus @default(ACTIVE)
|
||||
@@ -166,6 +167,16 @@ model User {
|
||||
schedulingPollVotes SchedulingPollVote[] @relation("PollVoter")
|
||||
schedulingPollComments SchedulingPollComment[] @relation("PollCommenter")
|
||||
|
||||
// Participant needs
|
||||
participantNeeds ParticipantNeeds? @relation("UserParticipantNeeds")
|
||||
|
||||
// Meeting agendas & action items
|
||||
agendasCreated MeetingAgenda[] @relation("AgendaCreator")
|
||||
minutesCreated MeetingMinutes[] @relation("MinutesCreator")
|
||||
minutesApproved MeetingMinutes[] @relation("MinutesApprover")
|
||||
actionItemsAssigned ActionItem[] @relation("ActionItemAssignee")
|
||||
actionItemsCreated ActionItem[] @relation("ActionItemCreator")
|
||||
|
||||
// Referral system
|
||||
inviteCodesCreated InviteCode[] @relation("InviteCodesCreated")
|
||||
referralsMade Referral[] @relation("ReferralsMade")
|
||||
@@ -736,6 +747,9 @@ model Shift {
|
||||
// Scheduling poll conversion
|
||||
convertedFromPoll SchedulingPoll? @relation("PollConvertedShift")
|
||||
|
||||
// Meeting agenda
|
||||
agenda MeetingAgenda? @relation("ShiftAgenda")
|
||||
|
||||
@@index([cutId])
|
||||
@@index([seriesId])
|
||||
@@map("shifts")
|
||||
@@ -750,6 +764,7 @@ enum SignupSource {
|
||||
AUTHENTICATED
|
||||
PUBLIC
|
||||
ADMIN
|
||||
POLL_CONVERSION
|
||||
}
|
||||
|
||||
model ShiftSignup {
|
||||
@@ -4168,6 +4183,7 @@ enum ContactSource {
|
||||
SHIFT_SIGNUP
|
||||
SMS_CONTACT
|
||||
DONATION
|
||||
POLL_VOTE
|
||||
MANUAL
|
||||
}
|
||||
|
||||
@@ -4194,6 +4210,7 @@ enum ContactActivityType {
|
||||
PROFILE_SELF_EDIT
|
||||
PROFILE_PHOTO_UPDATED
|
||||
USER_LOGIN
|
||||
POLL_VOTED
|
||||
}
|
||||
|
||||
model Contact {
|
||||
@@ -4203,6 +4220,7 @@ model Contact {
|
||||
lastName String?
|
||||
email String?
|
||||
phone String?
|
||||
pronouns String?
|
||||
|
||||
// CRM data
|
||||
tags Json @default("[]") // String array
|
||||
@@ -4247,6 +4265,8 @@ model Contact {
|
||||
connectionsTo ContactConnection[] @relation("ConnectionTo")
|
||||
activities ContactActivity[]
|
||||
smsConversations SmsConversation[] @relation("ContactSmsConversations")
|
||||
pollVotes SchedulingPollVote[] @relation("PollVoteContact")
|
||||
participantNeeds ParticipantNeeds? @relation("ContactParticipantNeeds")
|
||||
|
||||
@@index([email])
|
||||
@@index([phone])
|
||||
@@ -4404,7 +4424,16 @@ model SchedulingPoll {
|
||||
convertedShiftId String? @unique @map("converted_shift_id")
|
||||
convertedShift Shift? @relation("PollConvertedShift", fields: [convertedShiftId], references: [id], onDelete: SetNull)
|
||||
convertedGancioEventId Int? @map("converted_gancio_event_id")
|
||||
convertedCalendarItemId String? @map("converted_calendar_item_id")
|
||||
votingDeadline DateTime? @map("voting_deadline")
|
||||
autoFinalize Boolean @default(false) @map("auto_finalize")
|
||||
autoFinalizeThreshold Int? @map("auto_finalize_threshold")
|
||||
autoConvertToCalendar Boolean @default(false) @map("auto_convert_to_calendar")
|
||||
autoConvertToGancio Boolean @default(false) @map("auto_convert_to_gancio")
|
||||
autoConvertToShift Boolean @default(false) @map("auto_convert_to_shift")
|
||||
tieBreaker String @default("earliest") @map("tie_breaker")
|
||||
autoEnrollVoters Boolean @default(true) @map("auto_enroll_voters")
|
||||
autoFinalizeJobId String? @map("auto_finalize_job_id")
|
||||
allowAnonymous Boolean @default(true) @map("allow_anonymous")
|
||||
isPrivate Boolean @default(false) @map("is_private")
|
||||
notifyOnVote Boolean @default(true) @map("notify_on_vote")
|
||||
@@ -4416,6 +4445,7 @@ model SchedulingPoll {
|
||||
options SchedulingPollOption[] @relation("PollOptions")
|
||||
votes SchedulingPollVote[] @relation("PollVotes")
|
||||
comments SchedulingPollComment[] @relation("PollComments")
|
||||
agenda MeetingAgenda? @relation("PollAgenda")
|
||||
|
||||
@@index([createdByUserId])
|
||||
@@index([status])
|
||||
@@ -4451,6 +4481,8 @@ model SchedulingPollVote {
|
||||
voterName String @map("voter_name")
|
||||
voterEmail String? @map("voter_email")
|
||||
voterToken String? @map("voter_token") // anonymous edit access (cuid)
|
||||
contactId String? @map("contact_id")
|
||||
contact Contact? @relation("PollVoteContact", fields: [contactId], references: [id], onDelete: SetNull)
|
||||
value PollVoteValue
|
||||
createdAt DateTime @default(now()) @map("created_at")
|
||||
updatedAt DateTime @updatedAt @map("updated_at")
|
||||
@@ -4458,6 +4490,7 @@ model SchedulingPollVote {
|
||||
@@unique([optionId, userId])
|
||||
@@unique([optionId, voterToken])
|
||||
@@index([pollId])
|
||||
@@index([contactId])
|
||||
@@map("scheduling_poll_votes")
|
||||
}
|
||||
|
||||
@@ -4906,6 +4939,7 @@ enum CalendarShowDetailsTo {
|
||||
enum CalendarItemSource {
|
||||
MANUAL
|
||||
ICS_FEED
|
||||
POLL
|
||||
}
|
||||
|
||||
enum CalendarRecurrenceFrequency {
|
||||
@@ -5131,3 +5165,119 @@ model DocCollabState {
|
||||
|
||||
@@map("doc_collab_state")
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// PARTICIPANT NEEDS
|
||||
// ============================================================================
|
||||
|
||||
model ParticipantNeeds {
|
||||
id String @id @default(cuid())
|
||||
userId String? @unique @map("user_id")
|
||||
user User? @relation("UserParticipantNeeds", fields: [userId], references: [id], onDelete: SetNull)
|
||||
contactId String? @unique @map("contact_id")
|
||||
contact Contact? @relation("ContactParticipantNeeds", fields: [contactId], references: [id], onDelete: SetNull)
|
||||
|
||||
// Accessibility
|
||||
needsWheelchair Boolean @default(false) @map("needs_wheelchair")
|
||||
needsGroundFloor Boolean @default(false) @map("needs_ground_floor")
|
||||
needsHearingLoop Boolean @default(false) @map("needs_hearing_loop")
|
||||
needsSignLanguage Boolean @default(false) @map("needs_sign_language")
|
||||
otherAccessibility String? @db.Text @map("other_accessibility")
|
||||
|
||||
// Dietary
|
||||
isVegan Boolean @default(false) @map("is_vegan")
|
||||
isVegetarian Boolean @default(false) @map("is_vegetarian")
|
||||
isGlutenFree Boolean @default(false) @map("is_gluten_free")
|
||||
isHalal Boolean @default(false) @map("is_halal")
|
||||
isKosher Boolean @default(false) @map("is_kosher")
|
||||
hasNutAllergy Boolean @default(false) @map("has_nut_allergy")
|
||||
otherDietary String? @db.Text @map("other_dietary")
|
||||
|
||||
// Care barriers
|
||||
needsChildcare Boolean @default(false) @map("needs_childcare")
|
||||
childcareDetails String? @db.Text @map("childcare_details")
|
||||
needsTransportation Boolean @default(false) @map("needs_transportation")
|
||||
transportationNotes String? @db.Text @map("transportation_notes")
|
||||
|
||||
// Communication
|
||||
preferredLanguage String? @default("en") @map("preferred_language")
|
||||
needsTranslation Boolean @default(false) @map("needs_translation")
|
||||
translationLanguage String? @map("translation_language")
|
||||
|
||||
// Consent
|
||||
visibilityConsent String @default("organizer_only") @map("visibility_consent")
|
||||
|
||||
updatedAt DateTime @updatedAt @map("updated_at")
|
||||
|
||||
@@map("participant_needs")
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// MEETING AGENDAS & ACTION ITEMS
|
||||
// ============================================================================
|
||||
|
||||
model MeetingAgenda {
|
||||
id String @id @default(cuid())
|
||||
shiftId String? @unique @map("shift_id")
|
||||
shift Shift? @relation("ShiftAgenda", fields: [shiftId], references: [id], onDelete: SetNull)
|
||||
pollId String? @unique @map("poll_id")
|
||||
poll SchedulingPoll? @relation("PollAgenda", fields: [pollId], references: [id], onDelete: SetNull)
|
||||
|
||||
title String
|
||||
items Json @default("[]") // Array<{ id, title, durationMinutes, presenterUserId, notes, order }>
|
||||
status String @default("draft") // "draft" | "active" | "completed"
|
||||
|
||||
minutes MeetingMinutes?
|
||||
actionItems ActionItem[]
|
||||
|
||||
createdByUserId String @map("created_by_user_id")
|
||||
createdBy User @relation("AgendaCreator", fields: [createdByUserId], references: [id])
|
||||
createdAt DateTime @default(now()) @map("created_at")
|
||||
updatedAt DateTime @updatedAt @map("updated_at")
|
||||
|
||||
@@map("meeting_agendas")
|
||||
}
|
||||
|
||||
model MeetingMinutes {
|
||||
id String @id @default(cuid())
|
||||
agendaId String @unique @map("agenda_id")
|
||||
agenda MeetingAgenda @relation(fields: [agendaId], references: [id], onDelete: Cascade)
|
||||
|
||||
notes String @db.Text
|
||||
decisions Json @default("[]") // Array<{ id, text, passed: boolean }>
|
||||
attendees Json @default("[]") // Array<{ name, pronouns, userId? }>
|
||||
|
||||
approvedAt DateTime? @map("approved_at")
|
||||
approvedByUserId String? @map("approved_by_user_id")
|
||||
approvedBy User? @relation("MinutesApprover", fields: [approvedByUserId], references: [id], onDelete: SetNull)
|
||||
createdByUserId String @map("created_by_user_id")
|
||||
createdBy User @relation("MinutesCreator", fields: [createdByUserId], references: [id])
|
||||
createdAt DateTime @default(now()) @map("created_at")
|
||||
updatedAt DateTime @updatedAt @map("updated_at")
|
||||
|
||||
@@map("meeting_minutes")
|
||||
}
|
||||
|
||||
model ActionItem {
|
||||
id String @id @default(cuid())
|
||||
agendaId String? @map("agenda_id")
|
||||
agenda MeetingAgenda? @relation(fields: [agendaId], references: [id], onDelete: SetNull)
|
||||
|
||||
title String
|
||||
description String? @db.Text
|
||||
assigneeUserId String? @map("assignee_user_id")
|
||||
assignee User? @relation("ActionItemAssignee", fields: [assigneeUserId], references: [id], onDelete: SetNull)
|
||||
dueDate DateTime? @map("due_date")
|
||||
status String @default("open") // "open" | "in_progress" | "done" | "blocked"
|
||||
priority String @default("normal") // "low" | "normal" | "high" | "urgent"
|
||||
|
||||
completedAt DateTime? @map("completed_at")
|
||||
createdByUserId String @map("created_by_user_id")
|
||||
createdBy User @relation("ActionItemCreator", fields: [createdByUserId], references: [id])
|
||||
createdAt DateTime @default(now()) @map("created_at")
|
||||
updatedAt DateTime @updatedAt @map("updated_at")
|
||||
|
||||
@@index([assigneeUserId, status])
|
||||
@@index([dueDate])
|
||||
@@map("action_items")
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user