Add ticketed events, Jitsi meeting integration, social features, and calendar system
- Ticketed events: full CRUD, ticket tiers (free/paid/donation), Stripe checkout, QR-based check-in scanner, public event pages, ticket confirmation emails - Event formats: IN_PERSON/ONLINE/HYBRID with auto Jitsi meeting room lifecycle, ticket-gated meeting access, moderator JWT tokens, feature-flag guarded - Social engagement: challenges with scoring/leaderboards, referral tracking, volunteer spotlight, impact stories, campaign celebrations, wall of fame - Social calendar: personal calendar layers, shared calendar items with recurrence, scheduling polls, mobile day view - MCP server: events tool pack with full admin CRUD + meeting token generation - Unified calendar: eventFormat-aware tags, online event indicators - Updated docs site, pangolin configs, and various admin UI improvements Bunker Admin
This commit is contained in:
@@ -0,0 +1,249 @@
|
||||
-- CreateEnum
|
||||
CREATE TYPE "ImpactStoryType" AS ENUM ('MILESTONE', 'VICTORY', 'RESPONSE', 'CUSTOM');
|
||||
|
||||
-- CreateEnum
|
||||
CREATE TYPE "ImpactStoryStatus" AS ENUM ('DRAFT', 'PUBLISHED', 'ARCHIVED');
|
||||
|
||||
-- CreateEnum
|
||||
CREATE TYPE "SpotlightStatus" AS ENUM ('NOMINATED', 'APPROVED', 'FEATURED', 'ARCHIVED');
|
||||
|
||||
-- CreateEnum
|
||||
CREATE TYPE "ChallengeStatus" AS ENUM ('DRAFT', 'UPCOMING', 'ACTIVE', 'COMPLETED', 'CANCELLED');
|
||||
|
||||
-- CreateEnum
|
||||
CREATE TYPE "ChallengeMetric" AS ENUM ('DOORS_KNOCKED', 'EMAILS_SENT', 'SHIFTS_ATTENDED', 'RESPONSES_SUBMITTED', 'REFERRALS_MADE');
|
||||
|
||||
-- AlterEnum
|
||||
-- This migration adds more than one value to an enum.
|
||||
-- With PostgreSQL versions 11 and earlier, this is not possible
|
||||
-- in a single migration. This can be worked around by creating
|
||||
-- multiple migrations, each migration adding only one value to
|
||||
-- the enum.
|
||||
|
||||
|
||||
ALTER TYPE "NotificationType" ADD VALUE 'impact_story';
|
||||
ALTER TYPE "NotificationType" ADD VALUE 'referral_completed';
|
||||
ALTER TYPE "NotificationType" ADD VALUE 'challenge_update';
|
||||
|
||||
-- AlterTable
|
||||
ALTER TABLE "privacy_settings" ADD COLUMN "show_on_leaderboard" BOOLEAN DEFAULT true;
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "invite_codes" (
|
||||
"id" TEXT NOT NULL,
|
||||
"code" TEXT NOT NULL,
|
||||
"created_by_user_id" TEXT NOT NULL,
|
||||
"max_uses" INTEGER NOT NULL DEFAULT 0,
|
||||
"used_count" INTEGER NOT NULL DEFAULT 0,
|
||||
"expires_at" TIMESTAMP(3),
|
||||
"is_active" BOOLEAN NOT NULL DEFAULT true,
|
||||
"note" TEXT,
|
||||
"created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
|
||||
CONSTRAINT "invite_codes_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "referrals" (
|
||||
"id" SERIAL NOT NULL,
|
||||
"referrer_id" TEXT NOT NULL,
|
||||
"referred_user_id" TEXT NOT NULL,
|
||||
"invite_code_id" TEXT,
|
||||
"referral_source" TEXT,
|
||||
"completed_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
|
||||
CONSTRAINT "referrals_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "impact_stories" (
|
||||
"id" TEXT NOT NULL,
|
||||
"campaign_id" TEXT NOT NULL,
|
||||
"type" "ImpactStoryType" NOT NULL,
|
||||
"status" "ImpactStoryStatus" NOT NULL DEFAULT 'DRAFT',
|
||||
"title" TEXT NOT NULL,
|
||||
"body" TEXT NOT NULL,
|
||||
"cover_image_url" TEXT,
|
||||
"milestone_value" INTEGER,
|
||||
"milestone_metric" TEXT,
|
||||
"created_by_user_id" TEXT,
|
||||
"published_at" TIMESTAMP(3),
|
||||
"created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"updated_at" TIMESTAMP(3) NOT NULL,
|
||||
|
||||
CONSTRAINT "impact_stories_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "campaign_milestones" (
|
||||
"id" SERIAL NOT NULL,
|
||||
"campaign_id" TEXT NOT NULL,
|
||||
"metric" TEXT NOT NULL,
|
||||
"threshold" INTEGER NOT NULL,
|
||||
"reached_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"story_generated" BOOLEAN NOT NULL DEFAULT false,
|
||||
|
||||
CONSTRAINT "campaign_milestones_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "volunteer_spotlights" (
|
||||
"id" TEXT NOT NULL,
|
||||
"user_id" TEXT NOT NULL,
|
||||
"status" "SpotlightStatus" NOT NULL DEFAULT 'NOMINATED',
|
||||
"headline" TEXT,
|
||||
"story" TEXT,
|
||||
"featured_month" TEXT,
|
||||
"nominated_by_user_id" TEXT,
|
||||
"approved_by_user_id" TEXT,
|
||||
"approved_at" TIMESTAMP(3),
|
||||
"created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"updated_at" TIMESTAMP(3) NOT NULL,
|
||||
|
||||
CONSTRAINT "volunteer_spotlights_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "challenges" (
|
||||
"id" TEXT NOT NULL,
|
||||
"title" TEXT NOT NULL,
|
||||
"description" TEXT,
|
||||
"metric" "ChallengeMetric" NOT NULL,
|
||||
"status" "ChallengeStatus" NOT NULL DEFAULT 'DRAFT',
|
||||
"starts_at" TIMESTAMP(3) NOT NULL,
|
||||
"ends_at" TIMESTAMP(3) NOT NULL,
|
||||
"min_team_size" INTEGER NOT NULL DEFAULT 2,
|
||||
"max_team_size" INTEGER NOT NULL DEFAULT 10,
|
||||
"max_teams" INTEGER,
|
||||
"created_by_user_id" TEXT NOT NULL,
|
||||
"created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"updated_at" TIMESTAMP(3) NOT NULL,
|
||||
|
||||
CONSTRAINT "challenges_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "challenge_teams" (
|
||||
"id" TEXT NOT NULL,
|
||||
"challenge_id" TEXT NOT NULL,
|
||||
"name" TEXT NOT NULL,
|
||||
"captain_user_id" TEXT NOT NULL,
|
||||
"score" INTEGER NOT NULL DEFAULT 0,
|
||||
"last_scored_at" TIMESTAMP(3),
|
||||
"created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
|
||||
CONSTRAINT "challenge_teams_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "challenge_team_members" (
|
||||
"id" SERIAL NOT NULL,
|
||||
"team_id" TEXT NOT NULL,
|
||||
"user_id" TEXT NOT NULL,
|
||||
"score" INTEGER NOT NULL DEFAULT 0,
|
||||
"joined_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
|
||||
CONSTRAINT "challenge_team_members_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateIndex
|
||||
CREATE UNIQUE INDEX "invite_codes_code_key" ON "invite_codes"("code");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "idx_invite_codes_code" ON "invite_codes"("code");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "idx_invite_codes_created_by" ON "invite_codes"("created_by_user_id");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE UNIQUE INDEX "referrals_referred_user_id_key" ON "referrals"("referred_user_id");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "idx_referrals_referrer" ON "referrals"("referrer_id");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "idx_impact_stories_campaign" ON "impact_stories"("campaign_id");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "idx_impact_stories_status" ON "impact_stories"("status");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "idx_impact_stories_type" ON "impact_stories"("type");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE UNIQUE INDEX "campaign_milestones_campaign_id_metric_threshold_key" ON "campaign_milestones"("campaign_id", "metric", "threshold");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "idx_volunteer_spotlights_user" ON "volunteer_spotlights"("user_id");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "idx_volunteer_spotlights_status" ON "volunteer_spotlights"("status");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "idx_volunteer_spotlights_month" ON "volunteer_spotlights"("featured_month");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "idx_challenges_status" ON "challenges"("status");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "idx_challenges_starts_at" ON "challenges"("starts_at");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "idx_challenge_teams_challenge" ON "challenge_teams"("challenge_id");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "idx_challenge_teams_score" ON "challenge_teams"("score");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE UNIQUE INDEX "challenge_teams_challenge_id_name_key" ON "challenge_teams"("challenge_id", "name");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "idx_challenge_team_members_user" ON "challenge_team_members"("user_id");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE UNIQUE INDEX "challenge_team_members_team_id_user_id_key" ON "challenge_team_members"("team_id", "user_id");
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "invite_codes" ADD CONSTRAINT "invite_codes_created_by_user_id_fkey" FOREIGN KEY ("created_by_user_id") REFERENCES "users"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "referrals" ADD CONSTRAINT "referrals_referrer_id_fkey" FOREIGN KEY ("referrer_id") REFERENCES "users"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "referrals" ADD CONSTRAINT "referrals_referred_user_id_fkey" FOREIGN KEY ("referred_user_id") REFERENCES "users"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "referrals" ADD CONSTRAINT "referrals_invite_code_id_fkey" FOREIGN KEY ("invite_code_id") REFERENCES "invite_codes"("id") ON DELETE SET NULL ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "impact_stories" ADD CONSTRAINT "impact_stories_campaign_id_fkey" FOREIGN KEY ("campaign_id") REFERENCES "campaigns"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "impact_stories" ADD CONSTRAINT "impact_stories_created_by_user_id_fkey" FOREIGN KEY ("created_by_user_id") REFERENCES "users"("id") ON DELETE SET NULL ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "campaign_milestones" ADD CONSTRAINT "campaign_milestones_campaign_id_fkey" FOREIGN KEY ("campaign_id") REFERENCES "campaigns"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "volunteer_spotlights" ADD CONSTRAINT "volunteer_spotlights_user_id_fkey" FOREIGN KEY ("user_id") REFERENCES "users"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "volunteer_spotlights" ADD CONSTRAINT "volunteer_spotlights_nominated_by_user_id_fkey" FOREIGN KEY ("nominated_by_user_id") REFERENCES "users"("id") ON DELETE SET NULL ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "volunteer_spotlights" ADD CONSTRAINT "volunteer_spotlights_approved_by_user_id_fkey" FOREIGN KEY ("approved_by_user_id") REFERENCES "users"("id") ON DELETE SET NULL ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "challenges" ADD CONSTRAINT "challenges_created_by_user_id_fkey" FOREIGN KEY ("created_by_user_id") REFERENCES "users"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "challenge_teams" ADD CONSTRAINT "challenge_teams_challenge_id_fkey" FOREIGN KEY ("challenge_id") REFERENCES "challenges"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "challenge_teams" ADD CONSTRAINT "challenge_teams_captain_user_id_fkey" FOREIGN KEY ("captain_user_id") REFERENCES "users"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "challenge_team_members" ADD CONSTRAINT "challenge_team_members_team_id_fkey" FOREIGN KEY ("team_id") REFERENCES "challenge_teams"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "challenge_team_members" ADD CONSTRAINT "challenge_team_members_user_id_fkey" FOREIGN KEY ("user_id") REFERENCES "users"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
|
||||
@@ -0,0 +1,176 @@
|
||||
-- CreateEnum
|
||||
CREATE TYPE "TicketedEventStatus" AS ENUM ('DRAFT', 'PENDING_APPROVAL', 'PUBLISHED', 'CANCELLED', 'COMPLETED');
|
||||
|
||||
-- CreateEnum
|
||||
CREATE TYPE "TicketedEventVisibility" AS ENUM ('PUBLIC', 'UNLISTED', 'PRIVATE');
|
||||
|
||||
-- CreateEnum
|
||||
CREATE TYPE "TicketTierType" AS ENUM ('PAID', 'FREE', 'DONATION');
|
||||
|
||||
-- CreateEnum
|
||||
CREATE TYPE "TicketStatus" AS ENUM ('VALID', 'CHECKED_IN', 'CANCELLED', 'REFUNDED');
|
||||
|
||||
-- AlterTable
|
||||
ALTER TABLE "site_settings" ADD COLUMN "enable_ticketed_events" BOOLEAN NOT NULL DEFAULT false,
|
||||
ADD COLUMN "require_event_approval" BOOLEAN NOT NULL DEFAULT true;
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "ticketed_events" (
|
||||
"id" TEXT NOT NULL,
|
||||
"slug" TEXT NOT NULL,
|
||||
"title" TEXT NOT NULL,
|
||||
"description" TEXT,
|
||||
"rich_description" TEXT,
|
||||
"date" DATE NOT NULL,
|
||||
"start_time" TEXT NOT NULL,
|
||||
"end_time" TEXT NOT NULL,
|
||||
"doors_open_time" TEXT,
|
||||
"venue_name" TEXT,
|
||||
"venue_address" TEXT,
|
||||
"latitude" DECIMAL(10,7),
|
||||
"longitude" DECIMAL(10,7),
|
||||
"status" "TicketedEventStatus" NOT NULL DEFAULT 'DRAFT',
|
||||
"visibility" "TicketedEventVisibility" NOT NULL DEFAULT 'PUBLIC',
|
||||
"invite_code" TEXT,
|
||||
"cover_image_url" TEXT,
|
||||
"max_attendees" INTEGER,
|
||||
"current_attendees" INTEGER NOT NULL DEFAULT 0,
|
||||
"gancio_event_id" INTEGER,
|
||||
"created_by_user_id" TEXT NOT NULL,
|
||||
"organizer_name" TEXT,
|
||||
"organizer_email" TEXT,
|
||||
"created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"updated_at" TIMESTAMP(3) NOT NULL,
|
||||
|
||||
CONSTRAINT "ticketed_events_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "ticket_tiers" (
|
||||
"id" TEXT NOT NULL,
|
||||
"event_id" TEXT NOT NULL,
|
||||
"name" TEXT NOT NULL,
|
||||
"description" TEXT,
|
||||
"tier_type" "TicketTierType" NOT NULL,
|
||||
"price_cad" INTEGER NOT NULL DEFAULT 0,
|
||||
"min_donation_cad" INTEGER,
|
||||
"max_quantity" INTEGER,
|
||||
"sold_count" INTEGER NOT NULL DEFAULT 0,
|
||||
"max_per_order" INTEGER NOT NULL DEFAULT 10,
|
||||
"sales_start_at" TIMESTAMP(3),
|
||||
"sales_end_at" TIMESTAMP(3),
|
||||
"sort_order" INTEGER NOT NULL DEFAULT 0,
|
||||
"is_active" BOOLEAN NOT NULL DEFAULT true,
|
||||
"created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"updated_at" TIMESTAMP(3) NOT NULL,
|
||||
|
||||
CONSTRAINT "ticket_tiers_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "tickets" (
|
||||
"id" TEXT NOT NULL,
|
||||
"ticket_code" TEXT NOT NULL,
|
||||
"token_hash" TEXT NOT NULL,
|
||||
"event_id" TEXT NOT NULL,
|
||||
"tier_id" TEXT NOT NULL,
|
||||
"order_id" TEXT,
|
||||
"holder_email" TEXT NOT NULL,
|
||||
"holder_name" TEXT,
|
||||
"user_id" TEXT,
|
||||
"status" "TicketStatus" NOT NULL DEFAULT 'VALID',
|
||||
"checked_in_at" TIMESTAMP(3),
|
||||
"checked_in_by_user_id" TEXT,
|
||||
"issued_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"updated_at" TIMESTAMP(3) NOT NULL,
|
||||
|
||||
CONSTRAINT "tickets_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "check_ins" (
|
||||
"id" TEXT NOT NULL,
|
||||
"ticket_id" TEXT NOT NULL,
|
||||
"event_id" TEXT NOT NULL,
|
||||
"checked_in_by_user_id" TEXT,
|
||||
"method" TEXT NOT NULL,
|
||||
"checked_in_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"notes" TEXT,
|
||||
|
||||
CONSTRAINT "check_ins_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateIndex
|
||||
CREATE UNIQUE INDEX "ticketed_events_slug_key" ON "ticketed_events"("slug");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE UNIQUE INDEX "ticketed_events_invite_code_key" ON "ticketed_events"("invite_code");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "idx_ticketed_events_status" ON "ticketed_events"("status");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "idx_ticketed_events_date" ON "ticketed_events"("date");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "idx_ticketed_events_visibility" ON "ticketed_events"("visibility");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "idx_ticketed_events_creator" ON "ticketed_events"("created_by_user_id");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "idx_ticket_tiers_event" ON "ticket_tiers"("event_id");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE UNIQUE INDEX "tickets_ticket_code_key" ON "tickets"("ticket_code");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE UNIQUE INDEX "tickets_token_hash_key" ON "tickets"("token_hash");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "idx_tickets_event" ON "tickets"("event_id");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "idx_tickets_tier" ON "tickets"("tier_id");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "idx_tickets_order" ON "tickets"("order_id");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "idx_tickets_holder_email" ON "tickets"("holder_email");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "idx_tickets_status" ON "tickets"("status");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "idx_checkins_event" ON "check_ins"("event_id");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "idx_checkins_ticket" ON "check_ins"("ticket_id");
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "ticketed_events" ADD CONSTRAINT "ticketed_events_created_by_user_id_fkey" FOREIGN KEY ("created_by_user_id") REFERENCES "users"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "ticket_tiers" ADD CONSTRAINT "ticket_tiers_event_id_fkey" FOREIGN KEY ("event_id") REFERENCES "ticketed_events"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "tickets" ADD CONSTRAINT "tickets_event_id_fkey" FOREIGN KEY ("event_id") REFERENCES "ticketed_events"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "tickets" ADD CONSTRAINT "tickets_tier_id_fkey" FOREIGN KEY ("tier_id") REFERENCES "ticket_tiers"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "tickets" ADD CONSTRAINT "tickets_order_id_fkey" FOREIGN KEY ("order_id") REFERENCES "orders"("id") ON DELETE SET NULL ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "tickets" ADD CONSTRAINT "tickets_user_id_fkey" FOREIGN KEY ("user_id") REFERENCES "users"("id") ON DELETE SET NULL ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "check_ins" ADD CONSTRAINT "check_ins_ticket_id_fkey" FOREIGN KEY ("ticket_id") REFERENCES "tickets"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "check_ins" ADD CONSTRAINT "check_ins_event_id_fkey" FOREIGN KEY ("event_id") REFERENCES "ticketed_events"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "check_ins" ADD CONSTRAINT "check_ins_checked_in_by_user_id_fkey" FOREIGN KEY ("checked_in_by_user_id") REFERENCES "users"("id") ON DELETE SET NULL ON UPDATE CASCADE;
|
||||
@@ -0,0 +1,12 @@
|
||||
-- CreateEnum
|
||||
CREATE TYPE "EventFormat" AS ENUM ('IN_PERSON', 'ONLINE', 'HYBRID');
|
||||
|
||||
-- AlterTable
|
||||
ALTER TABLE "ticketed_events" ADD COLUMN "event_format" "EventFormat" NOT NULL DEFAULT 'IN_PERSON',
|
||||
ADD COLUMN "meeting_id" TEXT;
|
||||
|
||||
-- CreateIndex
|
||||
CREATE UNIQUE INDEX "ticketed_events_meeting_id_key" ON "ticketed_events"("meeting_id");
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "ticketed_events" ADD CONSTRAINT "ticketed_events_meeting_id_fkey" FOREIGN KEY ("meeting_id") REFERENCES "meetings"("id") ON DELETE SET NULL ON UPDATE CASCADE;
|
||||
@@ -0,0 +1,258 @@
|
||||
-- CreateEnum
|
||||
CREATE TYPE "CalendarLayerType" AS ENUM ('SYSTEM', 'USER', 'EXTERNAL');
|
||||
|
||||
-- CreateEnum
|
||||
CREATE TYPE "CalendarSystemType" AS ENUM ('SHIFTS', 'TICKETS', 'POLLS', 'PUBLIC_EVENTS');
|
||||
|
||||
-- CreateEnum
|
||||
CREATE TYPE "CalendarVisibility" AS ENUM ('PRIVATE', 'FRIENDS', 'PUBLIC');
|
||||
|
||||
-- CreateEnum
|
||||
CREATE TYPE "CalendarItemType" AS ENUM ('EVENT', 'TIME_BLOCK', 'REMINDER');
|
||||
|
||||
-- CreateEnum
|
||||
CREATE TYPE "CalendarBusyStatus" AS ENUM ('BUSY', 'TENTATIVE', 'FREE');
|
||||
|
||||
-- CreateEnum
|
||||
CREATE TYPE "CalendarShowDetailsTo" AS ENUM ('NOBODY', 'FRIENDS', 'EVERYONE');
|
||||
|
||||
-- CreateEnum
|
||||
CREATE TYPE "CalendarItemSource" AS ENUM ('MANUAL', 'ICS_FEED');
|
||||
|
||||
-- CreateEnum
|
||||
CREATE TYPE "CalendarRecurrenceFrequency" AS ENUM ('DAILY', 'WEEKLY', 'BIWEEKLY', 'MONTHLY');
|
||||
|
||||
-- CreateEnum
|
||||
CREATE TYPE "CalendarFeedStatus" AS ENUM ('OK', 'ERROR', 'PENDING');
|
||||
|
||||
-- CreateEnum
|
||||
CREATE TYPE "CalendarFeedInterval" AS ENUM ('FIFTEEN_MIN', 'HOURLY', 'SIX_HOUR', 'DAILY');
|
||||
|
||||
-- CreateEnum
|
||||
CREATE TYPE "SharedViewType" AS ENUM ('MANUAL', 'ROLE_BASED');
|
||||
|
||||
-- CreateEnum
|
||||
CREATE TYPE "SharedViewScope" AS ENUM ('MEMBERS', 'PUBLIC');
|
||||
|
||||
-- CreateEnum
|
||||
CREATE TYPE "SharedViewMemberStatus" AS ENUM ('INVITED', 'ACCEPTED', 'DECLINED');
|
||||
|
||||
-- AlterTable
|
||||
ALTER TABLE "site_settings" ADD COLUMN "enable_social_calendar" BOOLEAN NOT NULL DEFAULT false;
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "calendar_layers" (
|
||||
"id" TEXT NOT NULL,
|
||||
"user_id" TEXT NOT NULL,
|
||||
"name" TEXT NOT NULL,
|
||||
"layer_type" "CalendarLayerType" NOT NULL,
|
||||
"system_type" "CalendarSystemType",
|
||||
"color" TEXT NOT NULL DEFAULT '#1890ff',
|
||||
"visibility" "CalendarVisibility" NOT NULL DEFAULT 'PRIVATE',
|
||||
"is_enabled" BOOLEAN NOT NULL DEFAULT true,
|
||||
"sort_order" INTEGER NOT NULL DEFAULT 0,
|
||||
"created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"updated_at" TIMESTAMP(3) NOT NULL,
|
||||
|
||||
CONSTRAINT "calendar_layers_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "calendar_items" (
|
||||
"id" TEXT NOT NULL,
|
||||
"user_id" TEXT NOT NULL,
|
||||
"layer_id" TEXT NOT NULL,
|
||||
"title" TEXT NOT NULL,
|
||||
"description" TEXT,
|
||||
"date" DATE NOT NULL,
|
||||
"start_time" TEXT NOT NULL,
|
||||
"end_time" TEXT NOT NULL,
|
||||
"is_all_day" BOOLEAN NOT NULL DEFAULT false,
|
||||
"item_type" "CalendarItemType" NOT NULL DEFAULT 'EVENT',
|
||||
"location" TEXT,
|
||||
"color" TEXT,
|
||||
"visibility" "CalendarVisibility",
|
||||
"busy_status" "CalendarBusyStatus" NOT NULL DEFAULT 'BUSY',
|
||||
"show_details_to" "CalendarShowDetailsTo" NOT NULL DEFAULT 'FRIENDS',
|
||||
"recurrence_rule" JSONB,
|
||||
"recurrence_end" TIMESTAMP(3),
|
||||
"series_id" TEXT,
|
||||
"is_exception" BOOLEAN NOT NULL DEFAULT false,
|
||||
"source_type" "CalendarItemSource" NOT NULL DEFAULT 'MANUAL',
|
||||
"source_id" TEXT,
|
||||
"created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"updated_at" TIMESTAMP(3) NOT NULL,
|
||||
|
||||
CONSTRAINT "calendar_items_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "calendar_feeds" (
|
||||
"id" TEXT NOT NULL,
|
||||
"user_id" TEXT NOT NULL,
|
||||
"name" TEXT NOT NULL,
|
||||
"url" TEXT NOT NULL,
|
||||
"layer_id" TEXT NOT NULL,
|
||||
"refresh_interval" "CalendarFeedInterval" NOT NULL DEFAULT 'HOURLY',
|
||||
"last_fetched_at" TIMESTAMP(3),
|
||||
"last_status" "CalendarFeedStatus" NOT NULL DEFAULT 'PENDING',
|
||||
"last_error" TEXT,
|
||||
"item_count" INTEGER NOT NULL DEFAULT 0,
|
||||
"created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"updated_at" TIMESTAMP(3) NOT NULL,
|
||||
|
||||
CONSTRAINT "calendar_feeds_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "shared_calendar_views" (
|
||||
"id" TEXT NOT NULL,
|
||||
"name" TEXT NOT NULL,
|
||||
"description" TEXT,
|
||||
"owner_id" TEXT NOT NULL,
|
||||
"view_type" "SharedViewType" NOT NULL DEFAULT 'MANUAL',
|
||||
"auto_include_roles" JSONB,
|
||||
"included_layer_types" JSONB NOT NULL DEFAULT '[]',
|
||||
"share_scope" "SharedViewScope" NOT NULL DEFAULT 'MEMBERS',
|
||||
"share_token" TEXT,
|
||||
"created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"updated_at" TIMESTAMP(3) NOT NULL,
|
||||
|
||||
CONSTRAINT "shared_calendar_views_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "shared_calendar_members" (
|
||||
"id" TEXT NOT NULL,
|
||||
"view_id" TEXT NOT NULL,
|
||||
"user_id" TEXT NOT NULL,
|
||||
"status" "SharedViewMemberStatus" NOT NULL DEFAULT 'INVITED',
|
||||
"color" TEXT NOT NULL DEFAULT '#1890ff',
|
||||
"joined_at" TIMESTAMP(3),
|
||||
|
||||
CONSTRAINT "shared_calendar_members_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "shared_view_comments" (
|
||||
"id" TEXT NOT NULL,
|
||||
"view_id" TEXT NOT NULL,
|
||||
"user_id" TEXT NOT NULL,
|
||||
"item_date" TEXT NOT NULL,
|
||||
"item_id" TEXT,
|
||||
"content" TEXT NOT NULL,
|
||||
"created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
|
||||
CONSTRAINT "shared_view_comments_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "shared_view_reactions" (
|
||||
"id" TEXT NOT NULL,
|
||||
"view_id" TEXT NOT NULL,
|
||||
"user_id" TEXT NOT NULL,
|
||||
"item_id" TEXT NOT NULL,
|
||||
"emoji" TEXT NOT NULL,
|
||||
"created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
|
||||
CONSTRAINT "shared_view_reactions_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "calendar_export_tokens" (
|
||||
"id" TEXT NOT NULL,
|
||||
"user_id" TEXT NOT NULL,
|
||||
"token" TEXT NOT NULL,
|
||||
"include_personal" BOOLEAN NOT NULL DEFAULT false,
|
||||
"include_layers" JSONB,
|
||||
"created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
|
||||
CONSTRAINT "calendar_export_tokens_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "idx_calendar_layers_user" ON "calendar_layers"("user_id");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE UNIQUE INDEX "idx_calendar_layers_user_system" ON "calendar_layers"("user_id", "system_type");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "idx_calendar_items_user_date" ON "calendar_items"("user_id", "date");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "idx_calendar_items_layer_date" ON "calendar_items"("layer_id", "date");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "idx_calendar_items_series" ON "calendar_items"("series_id");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "idx_calendar_items_source" ON "calendar_items"("source_type", "source_id");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE UNIQUE INDEX "calendar_feeds_layer_id_key" ON "calendar_feeds"("layer_id");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "idx_calendar_feeds_user" ON "calendar_feeds"("user_id");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE UNIQUE INDEX "shared_calendar_views_share_token_key" ON "shared_calendar_views"("share_token");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "idx_shared_views_owner" ON "shared_calendar_views"("owner_id");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "idx_shared_members_user" ON "shared_calendar_members"("user_id");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE UNIQUE INDEX "idx_shared_members_view_user" ON "shared_calendar_members"("view_id", "user_id");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "idx_shared_comments_view_date" ON "shared_view_comments"("view_id", "item_date");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE UNIQUE INDEX "idx_shared_reactions_unique" ON "shared_view_reactions"("view_id", "user_id", "item_id", "emoji");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE UNIQUE INDEX "calendar_export_tokens_token_key" ON "calendar_export_tokens"("token");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "idx_calendar_export_tokens_user" ON "calendar_export_tokens"("user_id");
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "calendar_layers" ADD CONSTRAINT "calendar_layers_user_id_fkey" FOREIGN KEY ("user_id") REFERENCES "users"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "calendar_items" ADD CONSTRAINT "calendar_items_user_id_fkey" FOREIGN KEY ("user_id") REFERENCES "users"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "calendar_items" ADD CONSTRAINT "calendar_items_layer_id_fkey" FOREIGN KEY ("layer_id") REFERENCES "calendar_layers"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "calendar_feeds" ADD CONSTRAINT "calendar_feeds_user_id_fkey" FOREIGN KEY ("user_id") REFERENCES "users"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "calendar_feeds" ADD CONSTRAINT "calendar_feeds_layer_id_fkey" FOREIGN KEY ("layer_id") REFERENCES "calendar_layers"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "shared_calendar_views" ADD CONSTRAINT "shared_calendar_views_owner_id_fkey" FOREIGN KEY ("owner_id") REFERENCES "users"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "shared_calendar_members" ADD CONSTRAINT "shared_calendar_members_view_id_fkey" FOREIGN KEY ("view_id") REFERENCES "shared_calendar_views"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "shared_calendar_members" ADD CONSTRAINT "shared_calendar_members_user_id_fkey" FOREIGN KEY ("user_id") REFERENCES "users"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "shared_view_comments" ADD CONSTRAINT "shared_view_comments_view_id_fkey" FOREIGN KEY ("view_id") REFERENCES "shared_calendar_views"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "shared_view_comments" ADD CONSTRAINT "shared_view_comments_user_id_fkey" FOREIGN KEY ("user_id") REFERENCES "users"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "shared_view_reactions" ADD CONSTRAINT "shared_view_reactions_view_id_fkey" FOREIGN KEY ("view_id") REFERENCES "shared_calendar_views"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "shared_view_reactions" ADD CONSTRAINT "shared_view_reactions_user_id_fkey" FOREIGN KEY ("user_id") REFERENCES "users"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "calendar_export_tokens" ADD CONSTRAINT "calendar_export_tokens_user_id_fkey" FOREIGN KEY ("user_id") REFERENCES "users"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
@@ -0,0 +1,11 @@
|
||||
-- AlterEnum
|
||||
-- This migration adds more than one value to an enum.
|
||||
-- With PostgreSQL versions 11 and earlier, this is not possible
|
||||
-- in a single migration. This can be worked around by creating
|
||||
-- multiple migrations, each migration adding only one value to
|
||||
-- the enum.
|
||||
|
||||
|
||||
ALTER TYPE "NotificationType" ADD VALUE 'shared_view_invite';
|
||||
ALTER TYPE "NotificationType" ADD VALUE 'shared_view_accepted';
|
||||
ALTER TYPE "NotificationType" ADD VALUE 'calendar_event_invite';
|
||||
@@ -160,6 +160,39 @@ model User {
|
||||
schedulingPollVotes SchedulingPollVote[] @relation("PollVoter")
|
||||
schedulingPollComments SchedulingPollComment[] @relation("PollCommenter")
|
||||
|
||||
// Referral system
|
||||
inviteCodesCreated InviteCode[] @relation("InviteCodesCreated")
|
||||
referralsMade Referral[] @relation("ReferralsMade")
|
||||
referredBy Referral? @relation("ReferredBy")
|
||||
|
||||
// Impact Stories
|
||||
impactStoriesCreated ImpactStory[] @relation("ImpactStoryCreator")
|
||||
|
||||
// Volunteer Spotlight
|
||||
spotlights VolunteerSpotlight[] @relation("SpotlightUser")
|
||||
spotlightNominations VolunteerSpotlight[] @relation("SpotlightNominator")
|
||||
spotlightApprovals VolunteerSpotlight[] @relation("SpotlightApprover")
|
||||
|
||||
// Team Challenges
|
||||
challengesCreated Challenge[] @relation("ChallengesCreated")
|
||||
challengeTeamsCaptained ChallengeTeam[] @relation("ChallengeTeamsCaptained")
|
||||
challengeParticipations ChallengeTeamMember[] @relation("ChallengeParticipations")
|
||||
|
||||
// Ticketed Events
|
||||
ticketedEventsCreated TicketedEvent[] @relation("EventCreator")
|
||||
ticketsHeld Ticket[] @relation("TicketHolder")
|
||||
checkInsMade CheckIn[] @relation("CheckInUser")
|
||||
|
||||
// Social Calendar
|
||||
calendarLayers CalendarLayer[] @relation("CalendarLayerOwner")
|
||||
calendarItems CalendarItem[] @relation("CalendarItemOwner")
|
||||
calendarFeeds CalendarFeed[] @relation("CalendarFeedOwner")
|
||||
sharedCalendarViewsOwned SharedCalendarView[] @relation("SharedViewOwner")
|
||||
sharedCalendarMemberships SharedCalendarMember[] @relation("SharedViewMember")
|
||||
sharedViewComments SharedViewComment[] @relation("SharedViewCommentUser")
|
||||
sharedViewReactions SharedViewReaction[] @relation("SharedViewReactionUser")
|
||||
calendarExportTokens CalendarExportToken[] @relation("CalendarExportTokenOwner")
|
||||
|
||||
@@map("users")
|
||||
}
|
||||
|
||||
@@ -249,6 +282,8 @@ model Campaign {
|
||||
customRecipients CustomRecipient[]
|
||||
calls Call[]
|
||||
smsCampaigns SmsCampaign[] @relation("SmsCampaigns")
|
||||
stories ImpactStory[] @relation("CampaignStories")
|
||||
milestones CampaignMilestone[] @relation("CampaignMilestones")
|
||||
|
||||
@@index([moderationStatus])
|
||||
@@index([isUserGenerated])
|
||||
@@ -898,6 +933,9 @@ model SiteSettings {
|
||||
enableSocial Boolean @default(false) @map("enable_social")
|
||||
enableMeet Boolean @default(false) @map("enable_meet")
|
||||
enableMeetingPlanner Boolean @default(false) @map("enable_meeting_planner")
|
||||
enableTicketedEvents Boolean @default(false) @map("enable_ticketed_events")
|
||||
enableSocialCalendar Boolean @default(false) @map("enable_social_calendar")
|
||||
requireEventApproval Boolean @default(true) @map("require_event_approval")
|
||||
autoSyncPeopleToMap Boolean @default(false) @map("auto_sync_people_to_map")
|
||||
|
||||
// SMS connection config (overrides env vars when non-empty)
|
||||
@@ -1465,6 +1503,12 @@ enum NotificationType {
|
||||
achievement
|
||||
system
|
||||
group_call
|
||||
impact_story
|
||||
referral_completed
|
||||
challenge_update
|
||||
shared_view_invite
|
||||
shared_view_accepted
|
||||
calendar_event_invite
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
@@ -2356,6 +2400,7 @@ model PrivacySettings {
|
||||
hidePublicFinishes Boolean? @default(false) @map("hide_public_finishes")
|
||||
allowFriendRequests Boolean? @default(true) @map("allow_friend_requests")
|
||||
closeFriendsOnlyWatching Boolean? @default(false) @map("close_friends_only_watching")
|
||||
showOnLeaderboard Boolean? @default(true) @map("show_on_leaderboard")
|
||||
createdAt DateTime @default(now()) @map("created_at")
|
||||
updatedAt DateTime? @map("updated_at")
|
||||
|
||||
@@ -3414,6 +3459,7 @@ model Order {
|
||||
product Product? @relation(fields: [productId], references: [id])
|
||||
donationPageId String? @map("donation_page_id")
|
||||
donationPage DonationPage? @relation("DonationPageOrders", fields: [donationPageId], references: [id], onDelete: SetNull)
|
||||
tickets Ticket[] @relation("TicketOrder")
|
||||
|
||||
@@index([userId], map: "idx_orders_user")
|
||||
@@index([productId], map: "idx_orders_product")
|
||||
@@ -4298,8 +4344,9 @@ model Meeting {
|
||||
endTime DateTime? @map("end_time")
|
||||
|
||||
// Reverse relations (one-to-one)
|
||||
shift Shift? @relation("ShiftMeeting")
|
||||
group SocialGroup? @relation("GroupMeeting")
|
||||
shift Shift? @relation("ShiftMeeting")
|
||||
group SocialGroup? @relation("GroupMeeting")
|
||||
ticketedEvent TicketedEvent? @relation("EventMeeting")
|
||||
|
||||
@@map("meetings")
|
||||
}
|
||||
@@ -4403,3 +4450,646 @@ model SchedulingPollComment {
|
||||
@@index([pollId])
|
||||
@@map("scheduling_poll_comments")
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// SOCIAL: INVITE / REFERRAL SYSTEM
|
||||
// ============================================================================
|
||||
|
||||
model InviteCode {
|
||||
id String @id @default(cuid())
|
||||
code String @unique
|
||||
createdByUserId String @map("created_by_user_id")
|
||||
maxUses Int @default(0) @map("max_uses") // 0 = unlimited
|
||||
usedCount Int @default(0) @map("used_count")
|
||||
expiresAt DateTime? @map("expires_at")
|
||||
isActive Boolean @default(true) @map("is_active")
|
||||
note String?
|
||||
createdAt DateTime @default(now()) @map("created_at")
|
||||
|
||||
// Relations
|
||||
createdBy User @relation("InviteCodesCreated", fields: [createdByUserId], references: [id])
|
||||
referrals Referral[] @relation("InviteCodeReferrals")
|
||||
|
||||
@@index([code], map: "idx_invite_codes_code")
|
||||
@@index([createdByUserId], map: "idx_invite_codes_created_by")
|
||||
@@map("invite_codes")
|
||||
}
|
||||
|
||||
model Referral {
|
||||
id Int @id @default(autoincrement())
|
||||
referrerId String @map("referrer_id")
|
||||
referredUserId String @unique @map("referred_user_id")
|
||||
inviteCodeId String? @map("invite_code_id")
|
||||
referralSource String? @map("referral_source")
|
||||
completedAt DateTime @default(now()) @map("completed_at")
|
||||
|
||||
// Relations
|
||||
referrer User @relation("ReferralsMade", fields: [referrerId], references: [id])
|
||||
referredUser User @relation("ReferredBy", fields: [referredUserId], references: [id])
|
||||
inviteCode InviteCode? @relation("InviteCodeReferrals", fields: [inviteCodeId], references: [id])
|
||||
|
||||
@@index([referrerId], map: "idx_referrals_referrer")
|
||||
@@map("referrals")
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// SOCIAL: IMPACT STORIES / CAMPAIGN VICTORIES
|
||||
// ============================================================================
|
||||
|
||||
enum ImpactStoryType {
|
||||
MILESTONE
|
||||
VICTORY
|
||||
RESPONSE
|
||||
CUSTOM
|
||||
}
|
||||
|
||||
enum ImpactStoryStatus {
|
||||
DRAFT
|
||||
PUBLISHED
|
||||
ARCHIVED
|
||||
}
|
||||
|
||||
model ImpactStory {
|
||||
id String @id @default(cuid())
|
||||
campaignId String @map("campaign_id")
|
||||
type ImpactStoryType
|
||||
status ImpactStoryStatus @default(DRAFT)
|
||||
title String
|
||||
body String @db.Text
|
||||
coverImageUrl String? @map("cover_image_url")
|
||||
milestoneValue Int? @map("milestone_value")
|
||||
milestoneMetric String? @map("milestone_metric")
|
||||
createdByUserId String? @map("created_by_user_id")
|
||||
publishedAt DateTime? @map("published_at")
|
||||
createdAt DateTime @default(now()) @map("created_at")
|
||||
updatedAt DateTime @updatedAt @map("updated_at")
|
||||
|
||||
// Relations
|
||||
campaign Campaign @relation("CampaignStories", fields: [campaignId], references: [id])
|
||||
createdBy User? @relation("ImpactStoryCreator", fields: [createdByUserId], references: [id])
|
||||
|
||||
@@index([campaignId], map: "idx_impact_stories_campaign")
|
||||
@@index([status], map: "idx_impact_stories_status")
|
||||
@@index([type], map: "idx_impact_stories_type")
|
||||
@@map("impact_stories")
|
||||
}
|
||||
|
||||
model CampaignMilestone {
|
||||
id Int @id @default(autoincrement())
|
||||
campaignId String @map("campaign_id")
|
||||
metric String // "emails_sent", "verified_responses"
|
||||
threshold Int
|
||||
reachedAt DateTime @default(now()) @map("reached_at")
|
||||
storyGenerated Boolean @default(false) @map("story_generated")
|
||||
|
||||
// Relations
|
||||
campaign Campaign @relation("CampaignMilestones", fields: [campaignId], references: [id])
|
||||
|
||||
@@unique([campaignId, metric, threshold])
|
||||
@@map("campaign_milestones")
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// SOCIAL: VOLUNTEER SPOTLIGHT / WALL OF FAME
|
||||
// ============================================================================
|
||||
|
||||
enum SpotlightStatus {
|
||||
NOMINATED
|
||||
APPROVED
|
||||
FEATURED
|
||||
ARCHIVED
|
||||
}
|
||||
|
||||
model VolunteerSpotlight {
|
||||
id String @id @default(cuid())
|
||||
userId String @map("user_id")
|
||||
status SpotlightStatus @default(NOMINATED)
|
||||
headline String?
|
||||
story String? @db.Text
|
||||
featuredMonth String? @map("featured_month") // "2026-03"
|
||||
nominatedByUserId String? @map("nominated_by_user_id")
|
||||
approvedByUserId String? @map("approved_by_user_id")
|
||||
approvedAt DateTime? @map("approved_at")
|
||||
createdAt DateTime @default(now()) @map("created_at")
|
||||
updatedAt DateTime @updatedAt @map("updated_at")
|
||||
|
||||
// Relations
|
||||
user User @relation("SpotlightUser", fields: [userId], references: [id])
|
||||
nominatedBy User? @relation("SpotlightNominator", fields: [nominatedByUserId], references: [id])
|
||||
approvedBy User? @relation("SpotlightApprover", fields: [approvedByUserId], references: [id])
|
||||
|
||||
@@index([userId], map: "idx_volunteer_spotlights_user")
|
||||
@@index([status], map: "idx_volunteer_spotlights_status")
|
||||
@@index([featuredMonth], map: "idx_volunteer_spotlights_month")
|
||||
@@map("volunteer_spotlights")
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// SOCIAL: TEAM CHALLENGES
|
||||
// ============================================================================
|
||||
|
||||
enum ChallengeStatus {
|
||||
DRAFT
|
||||
UPCOMING
|
||||
ACTIVE
|
||||
COMPLETED
|
||||
CANCELLED
|
||||
}
|
||||
|
||||
enum ChallengeMetric {
|
||||
DOORS_KNOCKED
|
||||
EMAILS_SENT
|
||||
SHIFTS_ATTENDED
|
||||
RESPONSES_SUBMITTED
|
||||
REFERRALS_MADE
|
||||
}
|
||||
|
||||
model Challenge {
|
||||
id String @id @default(cuid())
|
||||
title String
|
||||
description String? @db.Text
|
||||
metric ChallengeMetric
|
||||
status ChallengeStatus @default(DRAFT)
|
||||
startsAt DateTime @map("starts_at")
|
||||
endsAt DateTime @map("ends_at")
|
||||
minTeamSize Int @default(2) @map("min_team_size")
|
||||
maxTeamSize Int @default(10) @map("max_team_size")
|
||||
maxTeams Int? @map("max_teams") // null = unlimited
|
||||
createdByUserId String @map("created_by_user_id")
|
||||
createdAt DateTime @default(now()) @map("created_at")
|
||||
updatedAt DateTime @updatedAt @map("updated_at")
|
||||
|
||||
// Relations
|
||||
createdBy User @relation("ChallengesCreated", fields: [createdByUserId], references: [id])
|
||||
teams ChallengeTeam[] @relation("ChallengeTeams")
|
||||
|
||||
@@index([status], map: "idx_challenges_status")
|
||||
@@index([startsAt], map: "idx_challenges_starts_at")
|
||||
@@map("challenges")
|
||||
}
|
||||
|
||||
model ChallengeTeam {
|
||||
id String @id @default(cuid())
|
||||
challengeId String @map("challenge_id")
|
||||
name String
|
||||
captainUserId String @map("captain_user_id")
|
||||
score Int @default(0)
|
||||
lastScoredAt DateTime? @map("last_scored_at")
|
||||
createdAt DateTime @default(now()) @map("created_at")
|
||||
|
||||
// Relations
|
||||
challenge Challenge @relation("ChallengeTeams", fields: [challengeId], references: [id])
|
||||
captain User @relation("ChallengeTeamsCaptained", fields: [captainUserId], references: [id])
|
||||
members ChallengeTeamMember[] @relation("ChallengeTeamMembers")
|
||||
|
||||
@@unique([challengeId, name])
|
||||
@@index([challengeId], map: "idx_challenge_teams_challenge")
|
||||
@@index([score], map: "idx_challenge_teams_score")
|
||||
@@map("challenge_teams")
|
||||
}
|
||||
|
||||
model ChallengeTeamMember {
|
||||
id Int @id @default(autoincrement())
|
||||
teamId String @map("team_id")
|
||||
userId String @map("user_id")
|
||||
score Int @default(0)
|
||||
joinedAt DateTime @default(now()) @map("joined_at")
|
||||
|
||||
// Relations
|
||||
team ChallengeTeam @relation("ChallengeTeamMembers", fields: [teamId], references: [id], onDelete: Cascade)
|
||||
user User @relation("ChallengeParticipations", fields: [userId], references: [id])
|
||||
|
||||
@@unique([teamId, userId])
|
||||
@@index([userId], map: "idx_challenge_team_members_user")
|
||||
@@map("challenge_team_members")
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// TICKETED EVENTS
|
||||
// ============================================================================
|
||||
|
||||
enum TicketedEventStatus {
|
||||
DRAFT
|
||||
PENDING_APPROVAL
|
||||
PUBLISHED
|
||||
CANCELLED
|
||||
COMPLETED
|
||||
}
|
||||
|
||||
enum TicketedEventVisibility {
|
||||
PUBLIC
|
||||
UNLISTED
|
||||
PRIVATE
|
||||
}
|
||||
|
||||
enum TicketTierType {
|
||||
PAID
|
||||
FREE
|
||||
DONATION
|
||||
}
|
||||
|
||||
enum TicketStatus {
|
||||
VALID
|
||||
CHECKED_IN
|
||||
CANCELLED
|
||||
REFUNDED
|
||||
}
|
||||
|
||||
enum EventFormat {
|
||||
IN_PERSON
|
||||
ONLINE
|
||||
HYBRID
|
||||
}
|
||||
|
||||
model TicketedEvent {
|
||||
id String @id @default(cuid())
|
||||
slug String @unique
|
||||
title String
|
||||
description String? @db.Text
|
||||
richDescription String? @db.Text @map("rich_description")
|
||||
|
||||
// Schedule
|
||||
date DateTime @db.Date
|
||||
startTime String @map("start_time")
|
||||
endTime String @map("end_time")
|
||||
doorsOpenTime String? @map("doors_open_time")
|
||||
|
||||
// Venue
|
||||
venueName String? @map("venue_name")
|
||||
venueAddress String? @map("venue_address")
|
||||
latitude Decimal? @db.Decimal(10, 7)
|
||||
longitude Decimal? @db.Decimal(10, 7)
|
||||
|
||||
// Status
|
||||
status TicketedEventStatus @default(DRAFT)
|
||||
visibility TicketedEventVisibility @default(PUBLIC)
|
||||
inviteCode String? @unique @map("invite_code")
|
||||
|
||||
// Media
|
||||
coverImageUrl String? @map("cover_image_url")
|
||||
|
||||
// Capacity
|
||||
maxAttendees Int? @map("max_attendees")
|
||||
currentAttendees Int @default(0) @map("current_attendees")
|
||||
|
||||
// Gancio sync
|
||||
gancioEventId Int? @map("gancio_event_id")
|
||||
|
||||
// Format & Meeting
|
||||
eventFormat EventFormat @default(IN_PERSON) @map("event_format")
|
||||
meetingId String? @unique @map("meeting_id")
|
||||
|
||||
// Creator
|
||||
createdByUserId String @map("created_by_user_id")
|
||||
organizerName String? @map("organizer_name")
|
||||
organizerEmail String? @map("organizer_email")
|
||||
|
||||
createdAt DateTime @default(now()) @map("created_at")
|
||||
updatedAt DateTime @updatedAt @map("updated_at")
|
||||
|
||||
// Relations
|
||||
createdBy User @relation("EventCreator", fields: [createdByUserId], references: [id])
|
||||
meeting Meeting? @relation("EventMeeting", fields: [meetingId], references: [id], onDelete: SetNull)
|
||||
ticketTiers TicketTier[] @relation("EventTiers")
|
||||
tickets Ticket[] @relation("EventTickets")
|
||||
checkIns CheckIn[] @relation("EventCheckIns")
|
||||
|
||||
@@index([status], map: "idx_ticketed_events_status")
|
||||
@@index([date], map: "idx_ticketed_events_date")
|
||||
@@index([visibility], map: "idx_ticketed_events_visibility")
|
||||
@@index([createdByUserId], map: "idx_ticketed_events_creator")
|
||||
@@map("ticketed_events")
|
||||
}
|
||||
|
||||
model TicketTier {
|
||||
id String @id @default(cuid())
|
||||
eventId String @map("event_id")
|
||||
name String
|
||||
description String?
|
||||
tierType TicketTierType @map("tier_type")
|
||||
priceCAD Int @default(0) @map("price_cad") // In cents
|
||||
minDonationCAD Int? @map("min_donation_cad") // In cents
|
||||
maxQuantity Int? @map("max_quantity")
|
||||
soldCount Int @default(0) @map("sold_count")
|
||||
maxPerOrder Int @default(10) @map("max_per_order")
|
||||
salesStartAt DateTime? @map("sales_start_at")
|
||||
salesEndAt DateTime? @map("sales_end_at")
|
||||
sortOrder Int @default(0) @map("sort_order")
|
||||
isActive Boolean @default(true) @map("is_active")
|
||||
|
||||
createdAt DateTime @default(now()) @map("created_at")
|
||||
updatedAt DateTime @updatedAt @map("updated_at")
|
||||
|
||||
// Relations
|
||||
event TicketedEvent @relation("EventTiers", fields: [eventId], references: [id], onDelete: Cascade)
|
||||
tickets Ticket[] @relation("TierTickets")
|
||||
|
||||
@@index([eventId], map: "idx_ticket_tiers_event")
|
||||
@@map("ticket_tiers")
|
||||
}
|
||||
|
||||
model Ticket {
|
||||
id String @id @default(cuid())
|
||||
ticketCode String @unique @map("ticket_code")
|
||||
tokenHash String @unique @map("token_hash")
|
||||
eventId String @map("event_id")
|
||||
tierId String @map("tier_id")
|
||||
orderId String? @map("order_id")
|
||||
holderEmail String @map("holder_email")
|
||||
holderName String? @map("holder_name")
|
||||
userId String? @map("user_id")
|
||||
status TicketStatus @default(VALID)
|
||||
checkedInAt DateTime? @map("checked_in_at")
|
||||
checkedInByUserId String? @map("checked_in_by_user_id")
|
||||
issuedAt DateTime @default(now()) @map("issued_at")
|
||||
updatedAt DateTime @updatedAt @map("updated_at")
|
||||
|
||||
// Relations
|
||||
event TicketedEvent @relation("EventTickets", fields: [eventId], references: [id])
|
||||
tier TicketTier @relation("TierTickets", fields: [tierId], references: [id])
|
||||
order Order? @relation("TicketOrder", fields: [orderId], references: [id])
|
||||
holder User? @relation("TicketHolder", fields: [userId], references: [id])
|
||||
checkIns CheckIn[] @relation("TicketCheckIns")
|
||||
|
||||
@@index([eventId], map: "idx_tickets_event")
|
||||
@@index([tierId], map: "idx_tickets_tier")
|
||||
@@index([orderId], map: "idx_tickets_order")
|
||||
@@index([holderEmail], map: "idx_tickets_holder_email")
|
||||
@@index([status], map: "idx_tickets_status")
|
||||
@@map("tickets")
|
||||
}
|
||||
|
||||
model CheckIn {
|
||||
id String @id @default(cuid())
|
||||
ticketId String @map("ticket_id")
|
||||
eventId String @map("event_id")
|
||||
checkedInByUserId String? @map("checked_in_by_user_id")
|
||||
method String // "QR" | "MANUAL" | "CODE"
|
||||
checkedInAt DateTime @default(now()) @map("checked_in_at")
|
||||
notes String?
|
||||
|
||||
// Relations
|
||||
ticket Ticket @relation("TicketCheckIns", fields: [ticketId], references: [id])
|
||||
event TicketedEvent @relation("EventCheckIns", fields: [eventId], references: [id])
|
||||
checkedInBy User? @relation("CheckInUser", fields: [checkedInByUserId], references: [id])
|
||||
|
||||
@@index([eventId], map: "idx_checkins_event")
|
||||
@@index([ticketId], map: "idx_checkins_ticket")
|
||||
@@map("check_ins")
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// SOCIAL CALENDAR
|
||||
// ============================================================================
|
||||
|
||||
enum CalendarLayerType {
|
||||
SYSTEM
|
||||
USER
|
||||
EXTERNAL
|
||||
}
|
||||
|
||||
enum CalendarSystemType {
|
||||
SHIFTS
|
||||
TICKETS
|
||||
POLLS
|
||||
PUBLIC_EVENTS
|
||||
}
|
||||
|
||||
enum CalendarVisibility {
|
||||
PRIVATE
|
||||
FRIENDS
|
||||
PUBLIC
|
||||
}
|
||||
|
||||
enum CalendarItemType {
|
||||
EVENT
|
||||
TIME_BLOCK
|
||||
REMINDER
|
||||
}
|
||||
|
||||
enum CalendarBusyStatus {
|
||||
BUSY
|
||||
TENTATIVE
|
||||
FREE
|
||||
}
|
||||
|
||||
enum CalendarShowDetailsTo {
|
||||
NOBODY
|
||||
FRIENDS
|
||||
EVERYONE
|
||||
}
|
||||
|
||||
enum CalendarItemSource {
|
||||
MANUAL
|
||||
ICS_FEED
|
||||
}
|
||||
|
||||
enum CalendarRecurrenceFrequency {
|
||||
DAILY
|
||||
WEEKLY
|
||||
BIWEEKLY
|
||||
MONTHLY
|
||||
}
|
||||
|
||||
enum CalendarFeedStatus {
|
||||
OK
|
||||
ERROR
|
||||
PENDING
|
||||
}
|
||||
|
||||
enum CalendarFeedInterval {
|
||||
FIFTEEN_MIN
|
||||
HOURLY
|
||||
SIX_HOUR
|
||||
DAILY
|
||||
}
|
||||
|
||||
enum SharedViewType {
|
||||
MANUAL
|
||||
ROLE_BASED
|
||||
}
|
||||
|
||||
enum SharedViewScope {
|
||||
MEMBERS
|
||||
PUBLIC
|
||||
}
|
||||
|
||||
enum SharedViewMemberStatus {
|
||||
INVITED
|
||||
ACCEPTED
|
||||
DECLINED
|
||||
}
|
||||
|
||||
model CalendarLayer {
|
||||
id String @id @default(cuid())
|
||||
userId String @map("user_id")
|
||||
name String
|
||||
layerType CalendarLayerType @map("layer_type")
|
||||
systemType CalendarSystemType? @map("system_type")
|
||||
color String @default("#1890ff")
|
||||
visibility CalendarVisibility @default(PRIVATE)
|
||||
isEnabled Boolean @default(true) @map("is_enabled")
|
||||
sortOrder Int @default(0) @map("sort_order")
|
||||
createdAt DateTime @default(now()) @map("created_at")
|
||||
updatedAt DateTime @updatedAt @map("updated_at")
|
||||
|
||||
// Relations
|
||||
user User @relation("CalendarLayerOwner", fields: [userId], references: [id], onDelete: Cascade)
|
||||
items CalendarItem[] @relation("CalendarLayerItems")
|
||||
feed CalendarFeed? @relation("CalendarFeedLayer")
|
||||
|
||||
@@unique([userId, systemType], map: "idx_calendar_layers_user_system")
|
||||
@@index([userId], map: "idx_calendar_layers_user")
|
||||
@@map("calendar_layers")
|
||||
}
|
||||
|
||||
model CalendarItem {
|
||||
id String @id @default(cuid())
|
||||
userId String @map("user_id")
|
||||
layerId String @map("layer_id")
|
||||
title String
|
||||
description String? @db.Text
|
||||
date DateTime @db.Date
|
||||
startTime String @map("start_time") // HH:MM
|
||||
endTime String @map("end_time") // HH:MM
|
||||
isAllDay Boolean @default(false) @map("is_all_day")
|
||||
itemType CalendarItemType @default(EVENT) @map("item_type")
|
||||
location String?
|
||||
color String?
|
||||
visibility CalendarVisibility? // null = inherit from layer
|
||||
busyStatus CalendarBusyStatus @default(BUSY) @map("busy_status")
|
||||
showDetailsTo CalendarShowDetailsTo @default(FRIENDS) @map("show_details_to")
|
||||
|
||||
// Recurrence
|
||||
recurrenceRule Json? @map("recurrence_rule")
|
||||
recurrenceEnd DateTime? @map("recurrence_end")
|
||||
seriesId String? @map("series_id")
|
||||
isException Boolean @default(false) @map("is_exception")
|
||||
|
||||
// Source tracking
|
||||
sourceType CalendarItemSource @default(MANUAL) @map("source_type")
|
||||
sourceId String? @map("source_id")
|
||||
|
||||
createdAt DateTime @default(now()) @map("created_at")
|
||||
updatedAt DateTime @updatedAt @map("updated_at")
|
||||
|
||||
// Relations
|
||||
user User @relation("CalendarItemOwner", fields: [userId], references: [id], onDelete: Cascade)
|
||||
layer CalendarLayer @relation("CalendarLayerItems", fields: [layerId], references: [id], onDelete: Cascade)
|
||||
|
||||
@@index([userId, date], map: "idx_calendar_items_user_date")
|
||||
@@index([layerId, date], map: "idx_calendar_items_layer_date")
|
||||
@@index([seriesId], map: "idx_calendar_items_series")
|
||||
@@index([sourceType, sourceId], map: "idx_calendar_items_source")
|
||||
@@map("calendar_items")
|
||||
}
|
||||
|
||||
model CalendarFeed {
|
||||
id String @id @default(cuid())
|
||||
userId String @map("user_id")
|
||||
name String
|
||||
url String
|
||||
layerId String @unique @map("layer_id")
|
||||
refreshInterval CalendarFeedInterval @default(HOURLY) @map("refresh_interval")
|
||||
lastFetchedAt DateTime? @map("last_fetched_at")
|
||||
lastStatus CalendarFeedStatus @default(PENDING) @map("last_status")
|
||||
lastError String? @map("last_error")
|
||||
itemCount Int @default(0) @map("item_count")
|
||||
createdAt DateTime @default(now()) @map("created_at")
|
||||
updatedAt DateTime @updatedAt @map("updated_at")
|
||||
|
||||
// Relations
|
||||
user User @relation("CalendarFeedOwner", fields: [userId], references: [id], onDelete: Cascade)
|
||||
layer CalendarLayer @relation("CalendarFeedLayer", fields: [layerId], references: [id], onDelete: Cascade)
|
||||
|
||||
@@index([userId], map: "idx_calendar_feeds_user")
|
||||
@@map("calendar_feeds")
|
||||
}
|
||||
|
||||
model SharedCalendarView {
|
||||
id String @id @default(cuid())
|
||||
name String
|
||||
description String? @db.Text
|
||||
ownerId String @map("owner_id")
|
||||
viewType SharedViewType @default(MANUAL) @map("view_type")
|
||||
autoIncludeRoles Json? @map("auto_include_roles")
|
||||
includedLayerTypes Json @default("[]") @map("included_layer_types")
|
||||
shareScope SharedViewScope @default(MEMBERS) @map("share_scope")
|
||||
shareToken String? @unique @map("share_token")
|
||||
createdAt DateTime @default(now()) @map("created_at")
|
||||
updatedAt DateTime @updatedAt @map("updated_at")
|
||||
|
||||
// Relations
|
||||
owner User @relation("SharedViewOwner", fields: [ownerId], references: [id], onDelete: Cascade)
|
||||
members SharedCalendarMember[] @relation("SharedViewMembers")
|
||||
comments SharedViewComment[] @relation("SharedViewComments")
|
||||
reactions SharedViewReaction[] @relation("SharedViewReactions")
|
||||
|
||||
@@index([ownerId], map: "idx_shared_views_owner")
|
||||
@@map("shared_calendar_views")
|
||||
}
|
||||
|
||||
model SharedCalendarMember {
|
||||
id String @id @default(cuid())
|
||||
viewId String @map("view_id")
|
||||
userId String @map("user_id")
|
||||
status SharedViewMemberStatus @default(INVITED)
|
||||
color String @default("#1890ff")
|
||||
joinedAt DateTime? @map("joined_at")
|
||||
|
||||
// Relations
|
||||
view SharedCalendarView @relation("SharedViewMembers", fields: [viewId], references: [id], onDelete: Cascade)
|
||||
user User @relation("SharedViewMember", fields: [userId], references: [id], onDelete: Cascade)
|
||||
|
||||
@@unique([viewId, userId], map: "idx_shared_members_view_user")
|
||||
@@index([userId], map: "idx_shared_members_user")
|
||||
@@map("shared_calendar_members")
|
||||
}
|
||||
|
||||
model SharedViewComment {
|
||||
id String @id @default(cuid())
|
||||
viewId String @map("view_id")
|
||||
userId String @map("user_id")
|
||||
itemDate String @map("item_date") // YYYY-MM-DD
|
||||
itemId String? @map("item_id") // specific item reference
|
||||
content String @db.Text
|
||||
createdAt DateTime @default(now()) @map("created_at")
|
||||
|
||||
// Relations
|
||||
view SharedCalendarView @relation("SharedViewComments", fields: [viewId], references: [id], onDelete: Cascade)
|
||||
user User @relation("SharedViewCommentUser", fields: [userId], references: [id], onDelete: Cascade)
|
||||
|
||||
@@index([viewId, itemDate], map: "idx_shared_comments_view_date")
|
||||
@@map("shared_view_comments")
|
||||
}
|
||||
|
||||
model SharedViewReaction {
|
||||
id String @id @default(cuid())
|
||||
viewId String @map("view_id")
|
||||
userId String @map("user_id")
|
||||
itemId String @map("item_id")
|
||||
emoji String
|
||||
createdAt DateTime @default(now()) @map("created_at")
|
||||
|
||||
// Relations
|
||||
view SharedCalendarView @relation("SharedViewReactions", fields: [viewId], references: [id], onDelete: Cascade)
|
||||
user User @relation("SharedViewReactionUser", fields: [userId], references: [id], onDelete: Cascade)
|
||||
|
||||
@@unique([viewId, userId, itemId, emoji], map: "idx_shared_reactions_unique")
|
||||
@@map("shared_view_reactions")
|
||||
}
|
||||
|
||||
model CalendarExportToken {
|
||||
id String @id @default(cuid())
|
||||
userId String @map("user_id")
|
||||
token String @unique
|
||||
includePersonal Boolean @default(false) @map("include_personal")
|
||||
includeLayers Json? @map("include_layers")
|
||||
createdAt DateTime @default(now()) @map("created_at")
|
||||
|
||||
// Relations
|
||||
user User @relation("CalendarExportTokenOwner", fields: [userId], references: [id], onDelete: Cascade)
|
||||
|
||||
@@index([userId], map: "idx_calendar_export_tokens_user")
|
||||
@@map("calendar_export_tokens")
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user