rallly/apps/web/src/components/poll/manage-poll.tsx
2024-11-24 15:08:29 +00:00

279 lines
8.2 KiB
TypeScript

import { usePostHog } from "@rallly/posthog/client";
import { Button } from "@rallly/ui/button";
import { useDialog } from "@rallly/ui/dialog";
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuItemIconLabel,
DropdownMenuSeparator,
DropdownMenuTrigger,
} from "@rallly/ui/dropdown-menu";
import { Icon } from "@rallly/ui/icon";
import {
CalendarCheck2Icon,
ChevronDownIcon,
CopyIcon,
DownloadIcon,
PauseIcon,
PencilIcon,
PlayIcon,
RotateCcwIcon,
Settings2Icon,
SettingsIcon,
TableIcon,
TrashIcon,
} from "lucide-react";
import Link from "next/link";
import * as React from "react";
import { DuplicateDialog } from "@/app/[locale]/poll/[urlId]/duplicate-dialog";
import { trpc } from "@/app/providers";
import { PayWallDialog } from "@/components/pay-wall-dialog";
import { FinalizePollDialog } from "@/components/poll/manage-poll/finalize-poll-dialog";
import { ProFeatureBadge } from "@/components/pro-feature-badge";
import { Trans } from "@/components/trans";
import { usePlan } from "@/contexts/plan";
import { usePoll } from "@/contexts/poll";
import { DeletePollDialog } from "./manage-poll/delete-poll-dialog";
import { useCsvExporter } from "./manage-poll/use-csv-exporter";
function PauseResumeToggle() {
const poll = usePoll();
const queryClient = trpc.useUtils();
const resume = trpc.polls.resume.useMutation({
onSuccess: (_data, vars) => {
queryClient.polls.get.setData({ urlId: vars.pollId }, (oldData) => {
if (!oldData) return oldData;
return {
...oldData,
status: "live",
};
});
},
});
const pause = trpc.polls.pause.useMutation({
onSuccess: (_data, vars) => {
queryClient.polls.get.setData({ urlId: vars.pollId }, (oldData) => {
if (!oldData) return oldData;
return {
...oldData,
status: "paused",
};
});
},
});
if (poll.status === "paused") {
return (
<DropdownMenuItem
onClick={() => {
resume.mutate(
{ pollId: poll.id },
{
onSuccess: () => {
queryClient.polls.get.setData({ urlId: poll.id }, (oldData) => {
if (!oldData) return oldData;
return {
...oldData,
status: "live",
};
});
},
},
);
}}
>
<Icon>
<PlayIcon />
</Icon>
<Trans i18nKey="resumePoll" />
</DropdownMenuItem>
);
} else {
return (
<DropdownMenuItem
onClick={() => {
pause.mutate(
{ pollId: poll.id },
{
onSuccess: () => {
queryClient.polls.get.setData({ urlId: poll.id }, (oldData) => {
if (!oldData) return oldData;
return {
...oldData,
status: "paused",
};
});
},
},
);
}}
>
<Icon>
<PauseIcon />
</Icon>
<Trans i18nKey="pausePoll" />
</DropdownMenuItem>
);
}
}
const ManagePoll: React.FunctionComponent<{
disabled?: boolean;
}> = ({ disabled }) => {
const poll = usePoll();
const queryClient = trpc.useUtils();
const reopen = trpc.polls.reopen.useMutation({
onMutate: () => {
queryClient.polls.get.setData({ urlId: poll.id }, (oldPoll) => {
if (!oldPoll) {
return;
}
return {
...oldPoll,
event: null,
};
});
},
});
const [showDeletePollDialog, setShowDeletePollDialog] = React.useState(false);
const duplicateDialog = useDialog();
const finalizeDialog = useDialog();
const paywallDialog = useDialog();
const plan = usePlan();
const posthog = usePostHog();
const { exportToCsv } = useCsvExporter();
return (
<>
<DropdownMenu modal={false}>
<DropdownMenuTrigger asChild={true}>
<Button disabled={disabled}>
<Icon>
<SettingsIcon />
</Icon>
<Trans i18nKey="manage" />
<Icon>
<ChevronDownIcon />
</Icon>
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent align="end">
<DropdownMenuItem asChild>
<Link href={`/poll/${poll.id}/edit-details`}>
<DropdownMenuItemIconLabel icon={PencilIcon}>
<Trans i18nKey="editDetails" />
</DropdownMenuItemIconLabel>
</Link>
</DropdownMenuItem>
<DropdownMenuItem asChild>
<Link href={`/poll/${poll.id}/edit-options`}>
<DropdownMenuItemIconLabel icon={TableIcon}>
<Trans i18nKey="editOptions" />
</DropdownMenuItemIconLabel>
</Link>
</DropdownMenuItem>
<DropdownMenuItem asChild>
<Link href={`/poll/${poll.id}/edit-settings`}>
<DropdownMenuItemIconLabel icon={Settings2Icon}>
<Trans i18nKey="editSettings" defaults="Edit settings" />
</DropdownMenuItemIconLabel>
</Link>
</DropdownMenuItem>
<DropdownMenuSeparator />
<>
{poll.status === "finalized" ? (
<DropdownMenuItem
onClick={() => {
reopen.mutate({ pollId: poll.id });
}}
>
<Icon>
<RotateCcwIcon />
</Icon>
<Trans i18nKey="reopenPoll" defaults="Reopen" />
</DropdownMenuItem>
) : (
<>
<DropdownMenuItem
disabled={!!poll.event}
onClick={() => {
if (plan === "free") {
paywallDialog.trigger();
posthog?.capture("trigger paywall", {
poll_id: poll.id,
from: "manage-poll",
action: "finalize",
});
} else {
finalizeDialog.trigger();
}
}}
>
<Icon>
<CalendarCheck2Icon />
</Icon>
<Trans i18nKey="finishPoll" defaults="Finalize" />
<ProFeatureBadge />
</DropdownMenuItem>
<PauseResumeToggle />
</>
)}
</>
<DropdownMenuSeparator />
<DropdownMenuItem onClick={exportToCsv}>
<DropdownMenuItemIconLabel icon={DownloadIcon}>
<Trans i18nKey="exportToCsv" />
</DropdownMenuItemIconLabel>
</DropdownMenuItem>
<DropdownMenuItem
onClick={() => {
if (plan === "free") {
paywallDialog.trigger();
posthog?.capture("trigger paywall", {
poll_id: poll.id,
action: "duplicate",
from: "manage-poll",
});
} else {
duplicateDialog.trigger();
}
}}
>
<DropdownMenuItemIconLabel icon={CopyIcon}>
<Trans i18nKey="duplicate" defaults="Duplicate" />
<ProFeatureBadge />
</DropdownMenuItemIconLabel>
</DropdownMenuItem>
<DropdownMenuSeparator />
<DropdownMenuItem
className="text-destructive"
onClick={() => {
setShowDeletePollDialog(true);
}}
>
<TrashIcon className="size-4 opacity-75" />
<Trans i18nKey="delete" />
</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
<DeletePollDialog
urlId={poll.adminUrlId}
open={showDeletePollDialog}
onOpenChange={setShowDeletePollDialog}
/>
<DuplicateDialog
pollId={poll.id}
pollTitle={poll.title}
{...duplicateDialog.dialogProps}
/>
<FinalizePollDialog {...finalizeDialog.dialogProps} />
<PayWallDialog {...paywallDialog.dialogProps} />
</>
);
};
export default ManagePoll;