mirror of
https://github.com/lukevella/rallly.git
synced 2025-05-03 12:16:04 +02:00
♻️ Update how we store poll status (#957)
This commit is contained in:
parent
7670db6778
commit
04211ac168
10 changed files with 108 additions and 186 deletions
|
@ -75,12 +75,7 @@ export function PollsPage() {
|
||||||
data.length > 0 ? (
|
data.length > 0 ? (
|
||||||
<div className="mx-auto grid max-w-3xl gap-3 sm:gap-4">
|
<div className="mx-auto grid max-w-3xl gap-3 sm:gap-4">
|
||||||
{data.map((poll) => {
|
{data.map((poll) => {
|
||||||
const { title, id: pollId, createdAt, closed: paused } = poll;
|
const { title, id: pollId, createdAt, status } = poll;
|
||||||
const status = poll.event
|
|
||||||
? "closed"
|
|
||||||
: paused
|
|
||||||
? "paused"
|
|
||||||
: "live";
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
key={poll.id}
|
key={poll.id}
|
||||||
|
|
|
@ -34,8 +34,6 @@ export const EventCard = () => {
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
const status = poll?.event ? "closed" : poll?.closed ? "paused" : "live";
|
|
||||||
|
|
||||||
if (!poll) {
|
if (!poll) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
@ -49,7 +47,7 @@ export const EventCard = () => {
|
||||||
/>
|
/>
|
||||||
<div className="bg-pattern p-4 sm:flex sm:flex-row-reverse sm:justify-between sm:px-6">
|
<div className="bg-pattern p-4 sm:flex sm:flex-row-reverse sm:justify-between sm:px-6">
|
||||||
<div className="mb-2">
|
<div className="mb-2">
|
||||||
<PollStatusBadge status={status} />
|
<PollStatusBadge status={poll.status} />
|
||||||
</div>
|
</div>
|
||||||
<div className="flex items-start justify-between">
|
<div className="flex items-start justify-between">
|
||||||
<div className="flex items-start gap-4 sm:gap-6">
|
<div className="flex items-start gap-4 sm:gap-6">
|
||||||
|
|
|
@ -42,7 +42,7 @@ import {
|
||||||
import ManagePoll from "@/components/poll/manage-poll";
|
import ManagePoll from "@/components/poll/manage-poll";
|
||||||
import NotificationsToggle from "@/components/poll/notifications-toggle";
|
import NotificationsToggle from "@/components/poll/notifications-toggle";
|
||||||
import { LegacyPollContextProvider } from "@/components/poll/poll-context-provider";
|
import { LegacyPollContextProvider } from "@/components/poll/poll-context-provider";
|
||||||
import { PollStatus } from "@/components/poll-status";
|
import { PollStatusLabel } from "@/components/poll-status";
|
||||||
import { Skeleton } from "@/components/skeleton";
|
import { Skeleton } from "@/components/skeleton";
|
||||||
import { Trans } from "@/components/trans";
|
import { Trans } from "@/components/trans";
|
||||||
import { useUser } from "@/components/user-provider";
|
import { useUser } from "@/components/user-provider";
|
||||||
|
@ -53,7 +53,6 @@ import { NextPageWithLayout } from "../../types";
|
||||||
|
|
||||||
const StatusControl = () => {
|
const StatusControl = () => {
|
||||||
const poll = usePoll();
|
const poll = usePoll();
|
||||||
const state = poll.event ? "closed" : poll.closed ? "paused" : "live";
|
|
||||||
const queryClient = trpc.useUtils();
|
const queryClient = trpc.useUtils();
|
||||||
const reopen = trpc.polls.reopen.useMutation({
|
const reopen = trpc.polls.reopen.useMutation({
|
||||||
onMutate: () => {
|
onMutate: () => {
|
||||||
|
@ -110,7 +109,7 @@ const StatusControl = () => {
|
||||||
<DropdownMenu modal={false}>
|
<DropdownMenu modal={false}>
|
||||||
<DropdownMenuTrigger asChild>
|
<DropdownMenuTrigger asChild>
|
||||||
<Button>
|
<Button>
|
||||||
<PollStatus status={state} />
|
<PollStatusLabel status={poll.status} />
|
||||||
<ChevronDownIcon className="h-4 w-4" />
|
<ChevronDownIcon className="h-4 w-4" />
|
||||||
</Button>
|
</Button>
|
||||||
</DropdownMenuTrigger>
|
</DropdownMenuTrigger>
|
||||||
|
|
|
@ -1,11 +1,10 @@
|
||||||
|
import { PollStatus } from "@rallly/database";
|
||||||
import { cn } from "@rallly/ui";
|
import { cn } from "@rallly/ui";
|
||||||
import { CheckCircleIcon, PauseCircleIcon, RadioIcon } from "lucide-react";
|
import { CheckCircleIcon, PauseCircleIcon, RadioIcon } from "lucide-react";
|
||||||
|
|
||||||
import { Trans } from "@/components/trans";
|
import { Trans } from "@/components/trans";
|
||||||
import { IconComponent } from "@/types";
|
import { IconComponent } from "@/types";
|
||||||
|
|
||||||
export type PollState = "live" | "paused" | "closed";
|
|
||||||
|
|
||||||
const LabelWithIcon = ({
|
const LabelWithIcon = ({
|
||||||
icon: Icon,
|
icon: Icon,
|
||||||
children,
|
children,
|
||||||
|
@ -23,11 +22,11 @@ const LabelWithIcon = ({
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export const PollStatus = ({
|
export const PollStatusLabel = ({
|
||||||
status,
|
status,
|
||||||
className,
|
className,
|
||||||
}: {
|
}: {
|
||||||
status: PollState;
|
status: PollStatus;
|
||||||
className?: string;
|
className?: string;
|
||||||
}) => {
|
}) => {
|
||||||
switch (status) {
|
switch (status) {
|
||||||
|
@ -43,7 +42,7 @@ export const PollStatus = ({
|
||||||
<Trans i18nKey="pollStatusPaused" defaults="Paused" />
|
<Trans i18nKey="pollStatusPaused" defaults="Paused" />
|
||||||
</LabelWithIcon>
|
</LabelWithIcon>
|
||||||
);
|
);
|
||||||
case "closed":
|
case "finalized":
|
||||||
return (
|
return (
|
||||||
<LabelWithIcon icon={CheckCircleIcon} className={className}>
|
<LabelWithIcon icon={CheckCircleIcon} className={className}>
|
||||||
<Trans i18nKey="pollStatusClosed" defaults="Finalized" />
|
<Trans i18nKey="pollStatusClosed" defaults="Finalized" />
|
||||||
|
@ -52,13 +51,13 @@ export const PollStatus = ({
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
export const PollStatusBadge = ({ status }: { status: PollState }) => {
|
export const PollStatusBadge = ({ status }: { status: PollStatus }) => {
|
||||||
return (
|
return (
|
||||||
<PollStatus
|
<PollStatusLabel
|
||||||
className={cn("rounded-full border py-0.5 pl-1.5 pr-3 text-sm", {
|
className={cn("rounded-full border py-0.5 pl-1.5 pr-3 text-sm", {
|
||||||
"border-blue-500 text-blue-500": status === "live",
|
"border-blue-500 text-blue-500": status === "live",
|
||||||
"border-gray-500 text-gray-500": status === "paused",
|
"border-gray-500 text-gray-500": status === "paused",
|
||||||
"border-green-500 text-green-500": status === "closed",
|
"border-green-500 text-green-500": status === "finalized",
|
||||||
})}
|
})}
|
||||||
status={status}
|
status={status}
|
||||||
/>
|
/>
|
||||||
|
|
|
@ -65,7 +65,7 @@ const NotificationsToggle: React.FunctionComponent = () => {
|
||||||
loading={watch.isLoading || unwatch.isLoading}
|
loading={watch.isLoading || unwatch.isLoading}
|
||||||
icon={isWatching ? BellRingIcon : BellOffIcon}
|
icon={isWatching ? BellRingIcon : BellOffIcon}
|
||||||
data-testid="notifications-toggle"
|
data-testid="notifications-toggle"
|
||||||
disabled={poll.demo || user.isGuest}
|
disabled={user.isGuest}
|
||||||
className="flex items-center gap-2 px-2.5"
|
className="flex items-center gap-2 px-2.5"
|
||||||
onClick={async () => {
|
onClick={async () => {
|
||||||
if (user.isGuest) {
|
if (user.isGuest) {
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import { User, VoteType } from "@rallly/database";
|
import { PollStatus, User, VoteType } from "@rallly/database";
|
||||||
|
|
||||||
export type GetPollApiResponse = {
|
export type GetPollApiResponse = {
|
||||||
id: string;
|
id: string;
|
||||||
|
@ -9,10 +9,9 @@ export type GetPollApiResponse = {
|
||||||
user: User | null;
|
user: User | null;
|
||||||
timeZone: string | null;
|
timeZone: string | null;
|
||||||
adminUrlId: string;
|
adminUrlId: string;
|
||||||
|
status: PollStatus;
|
||||||
participantUrlId: string;
|
participantUrlId: string;
|
||||||
closed: boolean;
|
closed: boolean;
|
||||||
legacy: boolean;
|
|
||||||
demo: boolean;
|
|
||||||
createdAt: Date;
|
createdAt: Date;
|
||||||
deleted: boolean;
|
deleted: boolean;
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,130 +0,0 @@
|
||||||
import { prisma, VoteType } from "@rallly/database";
|
|
||||||
import dayjs from "dayjs";
|
|
||||||
import { nanoid } from "nanoid";
|
|
||||||
|
|
||||||
const participantData: Array<{ name: string; votes: VoteType[] }> = [
|
|
||||||
{
|
|
||||||
name: "Reed",
|
|
||||||
votes: ["yes", "no", "yes", "no"],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "Susan",
|
|
||||||
votes: ["yes", "yes", "yes", "no"],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "Johnny",
|
|
||||||
votes: ["no", "no", "yes", "yes"],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "Ben",
|
|
||||||
votes: ["yes", "yes", "yes", "yes"],
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
const optionValues = ["2022-12-14", "2022-12-15", "2022-12-16", "2022-12-17"];
|
|
||||||
|
|
||||||
export const createPoll = async () => {
|
|
||||||
const pollId = nanoid();
|
|
||||||
|
|
||||||
const adminUrlId = nanoid();
|
|
||||||
|
|
||||||
const options: Array<{ start: Date; id: string }> = [];
|
|
||||||
|
|
||||||
for (let i = 0; i < optionValues.length; i++) {
|
|
||||||
options.push({ id: nanoid(), start: new Date(optionValues[i]) });
|
|
||||||
}
|
|
||||||
|
|
||||||
const participants: Array<{
|
|
||||||
name: string;
|
|
||||||
id: string;
|
|
||||||
userId: string;
|
|
||||||
createdAt: Date;
|
|
||||||
}> = [];
|
|
||||||
|
|
||||||
const votes: Array<{
|
|
||||||
optionId: string;
|
|
||||||
participantId: string;
|
|
||||||
type: VoteType;
|
|
||||||
}> = [];
|
|
||||||
|
|
||||||
for (let i = 0; i < participantData.length; i++) {
|
|
||||||
const { name, votes: participantVotes } = participantData[i];
|
|
||||||
const participantId = nanoid();
|
|
||||||
participants.push({
|
|
||||||
id: participantId,
|
|
||||||
name,
|
|
||||||
userId: "user-demo",
|
|
||||||
createdAt: dayjs()
|
|
||||||
.add(i * -1, "minutes")
|
|
||||||
.toDate(),
|
|
||||||
});
|
|
||||||
|
|
||||||
options.forEach((option, index) => {
|
|
||||||
votes.push({
|
|
||||||
optionId: option.id,
|
|
||||||
participantId,
|
|
||||||
type: participantVotes[index],
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
await prisma.poll.create({
|
|
||||||
data: {
|
|
||||||
id: pollId,
|
|
||||||
title: "Lunch Meeting",
|
|
||||||
location: "Starbucks, 901 New York Avenue",
|
|
||||||
description: `Hey everyone, please choose the dates when you are available to meet for our monthly get together. Looking forward to see you all!`,
|
|
||||||
demo: true,
|
|
||||||
adminUrlId,
|
|
||||||
participantUrlId: nanoid(),
|
|
||||||
userId: "guest-user",
|
|
||||||
options: {
|
|
||||||
createMany: {
|
|
||||||
data: options,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
participants: {
|
|
||||||
createMany: {
|
|
||||||
data: participants,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
votes: {
|
|
||||||
createMany: {
|
|
||||||
data: votes,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
return pollId;
|
|
||||||
};
|
|
||||||
|
|
||||||
export const deletePoll = async (pollId: string) => {
|
|
||||||
await prisma.$transaction([
|
|
||||||
prisma.vote.deleteMany({
|
|
||||||
where: {
|
|
||||||
pollId,
|
|
||||||
},
|
|
||||||
}),
|
|
||||||
prisma.option.deleteMany({
|
|
||||||
where: {
|
|
||||||
pollId,
|
|
||||||
},
|
|
||||||
}),
|
|
||||||
prisma.participant.deleteMany({
|
|
||||||
where: {
|
|
||||||
pollId,
|
|
||||||
},
|
|
||||||
}),
|
|
||||||
prisma.comment.deleteMany({
|
|
||||||
where: {
|
|
||||||
pollId,
|
|
||||||
},
|
|
||||||
}),
|
|
||||||
prisma.poll.deleteMany({
|
|
||||||
where: {
|
|
||||||
id: pollId,
|
|
||||||
},
|
|
||||||
}),
|
|
||||||
]);
|
|
||||||
};
|
|
|
@ -87,7 +87,6 @@ export const polls = router({
|
||||||
timeZone: input.timeZone,
|
timeZone: input.timeZone,
|
||||||
location: input.location,
|
location: input.location,
|
||||||
description: input.description,
|
description: input.description,
|
||||||
demo: input.demo,
|
|
||||||
adminUrlId: adminToken,
|
adminUrlId: adminToken,
|
||||||
participantUrlId,
|
participantUrlId,
|
||||||
userId: ctx.user.id,
|
userId: ctx.user.id,
|
||||||
|
@ -355,12 +354,11 @@ export const polls = router({
|
||||||
adminUrlId: true,
|
adminUrlId: true,
|
||||||
participantUrlId: true,
|
participantUrlId: true,
|
||||||
closed: true,
|
closed: true,
|
||||||
legacy: true,
|
status: true,
|
||||||
hideParticipants: true,
|
hideParticipants: true,
|
||||||
disableComments: true,
|
disableComments: true,
|
||||||
hideScores: true,
|
hideScores: true,
|
||||||
requireParticipantEmail: true,
|
requireParticipantEmail: true,
|
||||||
demo: true,
|
|
||||||
options: {
|
options: {
|
||||||
select: {
|
select: {
|
||||||
id: true,
|
id: true,
|
||||||
|
@ -436,6 +434,7 @@ export const polls = router({
|
||||||
timeZone: true,
|
timeZone: true,
|
||||||
adminUrlId: true,
|
adminUrlId: true,
|
||||||
participantUrlId: true,
|
participantUrlId: true,
|
||||||
|
status: true,
|
||||||
event: {
|
event: {
|
||||||
select: {
|
select: {
|
||||||
start: true,
|
start: true,
|
||||||
|
@ -551,15 +550,21 @@ export const polls = router({
|
||||||
eventStart = eventStart.tz(poll.timeZone, true);
|
eventStart = eventStart.tz(poll.timeZone, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
await prisma.event.create({
|
await prisma.poll.update({
|
||||||
|
where: {
|
||||||
|
id: input.pollId,
|
||||||
|
},
|
||||||
data: {
|
data: {
|
||||||
pollId: poll.id,
|
event: {
|
||||||
|
create: {
|
||||||
optionId: input.optionId,
|
optionId: input.optionId,
|
||||||
start: eventStart.toDate(),
|
start: eventStart.toDate(),
|
||||||
duration: option.duration,
|
duration: option.duration,
|
||||||
title: poll.title,
|
title: poll.title,
|
||||||
userId: ctx.user.id,
|
userId: ctx.user.id,
|
||||||
},
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const attendees = poll.participants.filter((p) =>
|
const attendees = poll.participants.filter((p) =>
|
||||||
|
@ -721,18 +726,16 @@ export const polls = router({
|
||||||
)
|
)
|
||||||
.mutation(async ({ input }) => {
|
.mutation(async ({ input }) => {
|
||||||
await prisma.$transaction([
|
await prisma.$transaction([
|
||||||
prisma.event.delete({
|
|
||||||
where: {
|
|
||||||
pollId: input.pollId,
|
|
||||||
},
|
|
||||||
}),
|
|
||||||
prisma.poll.update({
|
prisma.poll.update({
|
||||||
where: {
|
where: {
|
||||||
id: input.pollId,
|
id: input.pollId,
|
||||||
},
|
},
|
||||||
data: {
|
data: {
|
||||||
eventId: null,
|
event: {
|
||||||
closed: false,
|
delete: true,
|
||||||
|
},
|
||||||
|
status: "live",
|
||||||
|
closed: false, // @deprecated
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
]);
|
]);
|
||||||
|
@ -749,7 +752,8 @@ export const polls = router({
|
||||||
id: input.pollId,
|
id: input.pollId,
|
||||||
},
|
},
|
||||||
data: {
|
data: {
|
||||||
closed: true,
|
closed: true, // TODO (Luke Vella) [2023-12-05]: Remove this
|
||||||
|
status: "paused",
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
}),
|
}),
|
||||||
|
@ -827,6 +831,7 @@ export const polls = router({
|
||||||
},
|
},
|
||||||
data: {
|
data: {
|
||||||
closed: false,
|
closed: false,
|
||||||
|
status: "live",
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
}),
|
}),
|
||||||
|
|
|
@ -0,0 +1,50 @@
|
||||||
|
/*
|
||||||
|
Warnings:
|
||||||
|
|
||||||
|
- You are about to drop the column `demo` on the `polls` table. All the data in the column will be lost.
|
||||||
|
- You are about to drop the column `legacy` on the `polls` table. All the data in the column will be lost.
|
||||||
|
- A unique constraint covering the columns `[event_id]` on the table `polls` will be added. If there are existing duplicate values, this will fail.
|
||||||
|
|
||||||
|
*/
|
||||||
|
-- CreateEnum
|
||||||
|
CREATE TYPE "poll_status" AS ENUM ('live', 'paused', 'finalized');
|
||||||
|
|
||||||
|
-- AlterTable
|
||||||
|
ALTER TABLE "polls" DROP COLUMN "demo",
|
||||||
|
DROP COLUMN "legacy",
|
||||||
|
ADD COLUMN "status" "poll_status";
|
||||||
|
|
||||||
|
-- CreateIndex
|
||||||
|
CREATE UNIQUE INDEX "polls_event_id_key" ON "polls"("event_id");
|
||||||
|
|
||||||
|
-- Fix an issue where the "event_id" column was not being set
|
||||||
|
UPDATE "polls"
|
||||||
|
SET "event_id" = "events"."id"
|
||||||
|
FROM "events"
|
||||||
|
WHERE "events"."poll_id" = "polls"."id";
|
||||||
|
|
||||||
|
-- Set the "status" column to corressponding enum value
|
||||||
|
-- If "closed" is true, set to "paused"
|
||||||
|
-- If a poll has an "event_id", set to "finalized"
|
||||||
|
-- If a poll has a "deletedAt" date, set to "deleted"
|
||||||
|
-- Otherwise set to "live"
|
||||||
|
UPDATE "polls"
|
||||||
|
SET "status" = CASE
|
||||||
|
WHEN "closed" = true THEN 'paused'::poll_status
|
||||||
|
WHEN "event_id" IS NOT NULL THEN 'finalized'::poll_status
|
||||||
|
ELSE 'live'::poll_status
|
||||||
|
END;
|
||||||
|
|
||||||
|
-- Make the "status" column non-nullable and default to "live"
|
||||||
|
ALTER TABLE "polls"
|
||||||
|
ALTER COLUMN "status" SET NOT NULL,
|
||||||
|
ALTER COLUMN "status" SET DEFAULT 'live';
|
||||||
|
|
||||||
|
|
||||||
|
DROP INDEX "events_poll_id_idx";
|
||||||
|
|
||||||
|
-- DropIndex
|
||||||
|
DROP INDEX "events_poll_id_key";
|
||||||
|
|
||||||
|
-- AlterTable
|
||||||
|
ALTER TABLE "events" DROP COLUMN "poll_id";
|
|
@ -117,6 +117,14 @@ enum ParticipantVisibility {
|
||||||
@@map("participant_visibility")
|
@@map("participant_visibility")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
enum PollStatus {
|
||||||
|
live
|
||||||
|
paused
|
||||||
|
finalized
|
||||||
|
|
||||||
|
@@map("poll_status")
|
||||||
|
}
|
||||||
|
|
||||||
model Poll {
|
model Poll {
|
||||||
id String @id @unique @map("id")
|
id String @id @unique @map("id")
|
||||||
createdAt DateTime @default(now()) @map("created_at")
|
createdAt DateTime @default(now()) @map("created_at")
|
||||||
|
@ -125,46 +133,45 @@ model Poll {
|
||||||
title String
|
title String
|
||||||
description String?
|
description String?
|
||||||
location String?
|
location String?
|
||||||
user User? @relation(fields: [userId], references: [id])
|
|
||||||
userId String @map("user_id")
|
userId String @map("user_id")
|
||||||
votes Vote[]
|
|
||||||
timeZone String? @map("time_zone")
|
timeZone String? @map("time_zone")
|
||||||
options Option[]
|
closed Boolean @default(false) // @deprecated
|
||||||
participants Participant[]
|
status PollStatus @default(live)
|
||||||
watchers Watcher[]
|
|
||||||
demo Boolean @default(false)
|
|
||||||
comments Comment[]
|
|
||||||
legacy Boolean @default(false) // @deprecated
|
|
||||||
closed Boolean @default(false) // we use this to indicate whether a poll is paused
|
|
||||||
deleted Boolean @default(false)
|
deleted Boolean @default(false)
|
||||||
deletedAt DateTime? @map("deleted_at")
|
deletedAt DateTime? @map("deleted_at")
|
||||||
touchedAt DateTime @default(now()) @map("touched_at")
|
touchedAt DateTime @default(now()) @map("touched_at")
|
||||||
participantUrlId String @unique @map("participant_url_id")
|
participantUrlId String @unique @map("participant_url_id")
|
||||||
adminUrlId String @unique @map("admin_url_id")
|
adminUrlId String @unique @map("admin_url_id")
|
||||||
eventId String? @map("event_id")
|
eventId String? @unique @map("event_id")
|
||||||
event Event?
|
|
||||||
hideParticipants Boolean @default(false) @map("hide_participants")
|
hideParticipants Boolean @default(false) @map("hide_participants")
|
||||||
hideScores Boolean @default(false) @map("hide_scores")
|
hideScores Boolean @default(false) @map("hide_scores")
|
||||||
disableComments Boolean @default(false) @map("disable_comments")
|
disableComments Boolean @default(false) @map("disable_comments")
|
||||||
requireParticipantEmail Boolean @default(false) @map("require_participant_email")
|
requireParticipantEmail Boolean @default(false) @map("require_participant_email")
|
||||||
|
|
||||||
|
user User? @relation(fields: [userId], references: [id])
|
||||||
|
event Event? @relation(fields: [eventId], references: [id])
|
||||||
|
options Option[]
|
||||||
|
participants Participant[]
|
||||||
|
watchers Watcher[]
|
||||||
|
comments Comment[]
|
||||||
|
votes Vote[]
|
||||||
|
|
||||||
@@index([userId], type: Hash)
|
@@index([userId], type: Hash)
|
||||||
@@map("polls")
|
@@map("polls")
|
||||||
}
|
}
|
||||||
|
|
||||||
model Event {
|
model Event {
|
||||||
id String @id @default(cuid())
|
id String @id @default(cuid())
|
||||||
pollId String @unique @map("poll_id")
|
|
||||||
userId String @map("user_id")
|
userId String @map("user_id")
|
||||||
user User @relation(fields: [userId], references: [id])
|
user User @relation(fields: [userId], references: [id])
|
||||||
poll Poll @relation(fields: [pollId], references: [id])
|
|
||||||
optionId String @map("option_id")
|
optionId String @map("option_id")
|
||||||
title String
|
title String
|
||||||
start DateTime @db.Timestamp(0)
|
start DateTime @db.Timestamp(0)
|
||||||
duration Int @default(0) @map("duration_minutes")
|
duration Int @default(0) @map("duration_minutes")
|
||||||
createdAt DateTime @default(now()) @map("created_at")
|
createdAt DateTime @default(now()) @map("created_at")
|
||||||
|
|
||||||
@@index([pollId], type: Hash)
|
Poll Poll?
|
||||||
|
|
||||||
@@index([userId], type: Hash)
|
@@index([userId], type: Hash)
|
||||||
@@map("events")
|
@@map("events")
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Reference in a new issue