mirror of
https://github.com/lukevella/rallly.git
synced 2025-07-21 10:17:24 +02:00
✨ Better Pay-Wall Experience (#1357)
This commit is contained in:
parent
8e68a50caa
commit
39e15dd9eb
5 changed files with 219 additions and 249 deletions
|
@ -241,13 +241,6 @@
|
||||||
"dangerZoneAccount": "Delete your account permanently. This action cannot be undone.",
|
"dangerZoneAccount": "Delete your account permanently. This action cannot be undone.",
|
||||||
"upgradePromptTitle": "Upgrade to Pro",
|
"upgradePromptTitle": "Upgrade to Pro",
|
||||||
"upgradeOverlaySubtitle3": "Unlock these feature by upgrading to a Pro plan.",
|
"upgradeOverlaySubtitle3": "Unlock these feature by upgrading to a Pro plan.",
|
||||||
"finalizeFeatureDescription": "Select a final date for your event and notify participants.",
|
|
||||||
"duplicateTitle": "Duplicate",
|
|
||||||
"duplicateFeatureDescription": "Reuse dates and settings of a poll to create a new one.",
|
|
||||||
"advancedSettingsTitle": "Advanced Settings",
|
|
||||||
"advancedSettingsDescription": "Hide participants, hide scores, require participant email address.",
|
|
||||||
"keepPollsIndefinitely": "Keep Polls Indefinitely",
|
|
||||||
"keepPollsIndefinitelyDescription": "Inactive polls will not be auto-deleted.",
|
|
||||||
"verificationCodeSentTo": "We sent a verification code to <b>{email}</b>",
|
"verificationCodeSentTo": "We sent a verification code to <b>{email}</b>",
|
||||||
"home": "Home",
|
"home": "Home",
|
||||||
"groupPoll": "Group Poll",
|
"groupPoll": "Group Poll",
|
||||||
|
@ -282,5 +275,14 @@
|
||||||
"fileTooLarge": "File too large",
|
"fileTooLarge": "File too large",
|
||||||
"fileTooLargeDescription": "Please upload a file smaller than 2MB.",
|
"fileTooLargeDescription": "Please upload a file smaller than 2MB.",
|
||||||
"errorUploadPicture": "Failed to upload",
|
"errorUploadPicture": "Failed to upload",
|
||||||
"errorUploadPictureDescription": "There was an issue uploading your picture. Please try again later."
|
"errorUploadPictureDescription": "There was an issue uploading your picture. Please try again later.",
|
||||||
|
"featureNameFinalize": "Finalize Poll",
|
||||||
|
"featureNameDuplicate": "Duplicate Poll",
|
||||||
|
"featureNameAdvancedSettings": "Advanced Settings",
|
||||||
|
"featureNameExtendedPollLifetime": "Extended Poll Lifetime",
|
||||||
|
"12months": "12 months",
|
||||||
|
"savePercentage": "Save {percentage}%",
|
||||||
|
"1month": "1 month",
|
||||||
|
"subscribe": "Subscribe",
|
||||||
|
"cancelAnytime": "Cancel anytime from your <a>billing page</a>."
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,33 +1,38 @@
|
||||||
|
import { pricingData } from "@rallly/billing/pricing";
|
||||||
import { Badge } from "@rallly/ui/badge";
|
import { Badge } from "@rallly/ui/badge";
|
||||||
import { Button } from "@rallly/ui/button";
|
import { Dialog, DialogContent, DialogProps } from "@rallly/ui/dialog";
|
||||||
import { DialogClose, DialogContent } from "@rallly/ui/dialog";
|
import { RadioGroup, RadioGroupItem } from "@rallly/ui/radio-group";
|
||||||
import { m } from "framer-motion";
|
import { m } from "framer-motion";
|
||||||
import {
|
import { CheckIcon } from "lucide-react";
|
||||||
CalendarCheck2Icon,
|
|
||||||
ClockIcon,
|
|
||||||
CopyIcon,
|
|
||||||
Settings2Icon,
|
|
||||||
} from "lucide-react";
|
|
||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
|
import { useState } from "react";
|
||||||
|
|
||||||
import { Trans } from "@/components/trans";
|
import { Trans } from "@/components/trans";
|
||||||
import { usePlan } from "@/contexts/plan";
|
import { UpgradeButton } from "@/components/upgrade-button";
|
||||||
|
|
||||||
export function PayWallDialogContent({
|
const annualSavingsPercentage = (
|
||||||
children,
|
((pricingData.monthly.amount * 12 - pricingData.yearly.amount) /
|
||||||
}: {
|
(pricingData.monthly.amount * 12)) *
|
||||||
children?: React.ReactNode;
|
100
|
||||||
}) {
|
).toFixed(0);
|
||||||
const plan = usePlan();
|
|
||||||
|
|
||||||
if (plan === "free") {
|
const yearlyPrice = (pricingData.yearly.amount / 100).toFixed(2);
|
||||||
return (
|
const monthlyPrice = (pricingData.monthly.amount / 100).toFixed(2);
|
||||||
|
const monthlyPriceAnnualRate = (pricingData.yearly.amount / 100 / 12).toFixed(
|
||||||
|
2,
|
||||||
|
);
|
||||||
|
|
||||||
|
export function PayWallDialogContent(props: DialogProps) {
|
||||||
|
const [period, setPeriod] = useState("yearly");
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Dialog {...props}>
|
||||||
<DialogContent className="w-[600px] p-4">
|
<DialogContent className="w-[600px] p-4">
|
||||||
<article>
|
<div className="space-y-6">
|
||||||
<header className="p-4">
|
<header className="pt-4">
|
||||||
<m.div
|
<m.div
|
||||||
transition={{
|
transition={{
|
||||||
delay: 0.5,
|
delay: 0.2,
|
||||||
duration: 0.4,
|
duration: 0.4,
|
||||||
type: "spring",
|
type: "spring",
|
||||||
bounce: 0.5,
|
bounce: 0.5,
|
||||||
|
@ -41,111 +46,108 @@ export function PayWallDialogContent({
|
||||||
<Trans i18nKey="planPro" />
|
<Trans i18nKey="planPro" />
|
||||||
</Badge>
|
</Badge>
|
||||||
</m.div>
|
</m.div>
|
||||||
<h1 className="mb-1 mt-2 text-center text-xl font-bold">
|
<h1 className="mb-2 mt-4 text-center text-xl font-bold">
|
||||||
<Trans defaults="Upgrade to Pro" i18nKey="upgradePromptTitle" />
|
<Trans defaults="Upgrade to Pro" i18nKey="upgradePromptTitle" />
|
||||||
</h1>
|
</h1>
|
||||||
<p className="text-muted-foreground text-center text-sm leading-relaxed">
|
<p className="text-muted-foreground mb-4 text-center text-sm leading-relaxed">
|
||||||
<Trans
|
<Trans
|
||||||
i18nKey="upgradeOverlaySubtitle3"
|
i18nKey="upgradeOverlaySubtitle3"
|
||||||
defaults="Unlock these feature by upgrading to a Pro plan."
|
defaults="Unlock these feature by upgrading to a Pro plan."
|
||||||
/>
|
/>
|
||||||
</p>
|
</p>
|
||||||
</header>
|
<ul className="grid grid-cols-2 justify-center gap-2 text-center text-sm font-medium">
|
||||||
<section className="rounded-lg border bg-gray-50">
|
<li>
|
||||||
<ul className="divide-y text-left">
|
<CheckIcon className="mr-2 inline-block size-4 text-green-600" />
|
||||||
<li className="flex items-start gap-x-4 p-4">
|
<Trans i18nKey="featureNameFinalize" defaults="Finalize Poll" />
|
||||||
<div>
|
|
||||||
<div className="inline-flex rounded-lg bg-indigo-100 p-2">
|
|
||||||
<CalendarCheck2Icon className="size-4 text-indigo-600" />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<h3 className="mb-1 text-sm font-semibold">
|
|
||||||
<Trans defaults="Finalize" i18nKey="finalize" />
|
|
||||||
</h3>
|
|
||||||
<p className="text-muted-foreground text-pretty text-sm leading-relaxed">
|
|
||||||
<Trans
|
|
||||||
i18nKey="finalizeFeatureDescription"
|
|
||||||
defaults="Select a final date for your event and notify participants."
|
|
||||||
/>
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</li>
|
</li>
|
||||||
<li className="flex items-start gap-x-4 p-4">
|
<li>
|
||||||
<div className="inline-flex rounded-lg bg-violet-100 p-2">
|
<CheckIcon className="mr-2 inline-block size-4 text-green-600" />
|
||||||
<CopyIcon className="size-4 text-violet-600" />
|
<Trans
|
||||||
</div>
|
i18nKey="featureNameDuplicate"
|
||||||
<div>
|
defaults="Duplicate Poll"
|
||||||
<h3 className="mb-1 text-sm font-semibold">
|
/>
|
||||||
<Trans defaults="Duplicate" i18nKey="duplicateTitle" />
|
|
||||||
</h3>
|
|
||||||
<p className="text-muted-foreground leading-rel text-pretty text-sm">
|
|
||||||
<Trans
|
|
||||||
i18nKey="duplicateFeatureDescription"
|
|
||||||
defaults="Reuse dates and settings of a poll to create a new one."
|
|
||||||
/>
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</li>
|
</li>
|
||||||
<li className="flex items-start gap-x-4 p-4">
|
<li>
|
||||||
<div>
|
<CheckIcon className="mr-2 inline-block size-4 text-green-600" />
|
||||||
<div className="inline-flex rounded-lg bg-purple-100 p-2">
|
<Trans
|
||||||
<Settings2Icon className="size-4 text-purple-600" />
|
i18nKey="featureNameAdvancedSettings"
|
||||||
</div>
|
defaults="Advanced Settings"
|
||||||
</div>
|
/>
|
||||||
<div>
|
|
||||||
<h3 className="mb-1 text-sm font-semibold">
|
|
||||||
<Trans
|
|
||||||
defaults="Advanced Settings"
|
|
||||||
i18nKey="advancedSettingsTitle"
|
|
||||||
/>
|
|
||||||
</h3>
|
|
||||||
<p className="text-muted-foreground leading-rel text-pretty text-sm">
|
|
||||||
<Trans
|
|
||||||
i18nKey="advancedSettingsDescription"
|
|
||||||
defaults="Hide participants, hide scores, require participant email address."
|
|
||||||
/>
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</li>
|
</li>
|
||||||
<li className="flex items-start gap-x-4 p-4">
|
<li>
|
||||||
<div>
|
<CheckIcon className="mr-2 inline-block size-4 text-green-600" />
|
||||||
<div className="inline-flex rounded-lg bg-pink-100 p-2">
|
<Trans
|
||||||
<ClockIcon className="size-4 text-pink-600" />
|
i18nKey="featureNameExtendedPollLifetime"
|
||||||
</div>
|
defaults="Extended Poll Lifetime"
|
||||||
</div>
|
/>
|
||||||
<div>
|
|
||||||
<h3 className="mb-1 text-sm font-semibold">
|
|
||||||
<Trans
|
|
||||||
defaults="Keep Polls Indefinitely"
|
|
||||||
i18nKey="keepPollsIndefinitely"
|
|
||||||
/>
|
|
||||||
</h3>
|
|
||||||
<p className="text-muted-foreground leading-rel text-pretty text-sm">
|
|
||||||
<Trans
|
|
||||||
i18nKey="keepPollsIndefinitelyDescription"
|
|
||||||
defaults="Inactive polls will not be auto-deleted."
|
|
||||||
/>
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
|
</header>
|
||||||
|
<section>
|
||||||
|
<RadioGroup value={period} onValueChange={setPeriod}>
|
||||||
|
<li className="focus-within:ring-primary relative flex items-center justify-between rounded-lg border bg-gray-50 p-4 focus-within:ring-2">
|
||||||
|
<div className="space-y-1">
|
||||||
|
<div className="flex items-center gap-4">
|
||||||
|
<RadioGroupItem id="yearly" value="yearly" />
|
||||||
|
<label className="text-base font-semibold" htmlFor="yearly">
|
||||||
|
<span role="presentation" className="absolute inset-0" />
|
||||||
|
<Trans defaults="12 months" i18nKey="12months" />
|
||||||
|
</label>
|
||||||
|
<Badge variant="green">
|
||||||
|
<Trans
|
||||||
|
defaults="Save {percentage}%"
|
||||||
|
i18nKey="savePercentage"
|
||||||
|
values={{ percentage: annualSavingsPercentage }}
|
||||||
|
/>
|
||||||
|
</Badge>
|
||||||
|
</div>
|
||||||
|
<p className="text-muted-foreground flex items-baseline gap-1.5 pl-8 text-sm">
|
||||||
|
<span>${yearlyPrice}</span>
|
||||||
|
<span className="line-through opacity-50">
|
||||||
|
${((pricingData.monthly.amount * 12) / 100).toFixed(2)}
|
||||||
|
</span>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<p className="flex items-baseline gap-1">
|
||||||
|
<span className="text-xl font-semibold">
|
||||||
|
${monthlyPriceAnnualRate}
|
||||||
|
</span>
|
||||||
|
<span className="text-muted-foreground text-sm">/ mo</span>
|
||||||
|
</p>
|
||||||
|
</li>
|
||||||
|
<li className="focus-within:ring-primary relative flex items-center justify-between rounded-lg border bg-gray-50 p-4 focus-within:ring-2">
|
||||||
|
<div className="flex items-center gap-4">
|
||||||
|
<RadioGroupItem id="monthly" value="monthly" />
|
||||||
|
<label className="text-base font-semibold" htmlFor="monthly">
|
||||||
|
<span role="presentation" className="absolute inset-0" />
|
||||||
|
<Trans defaults="1 month" i18nKey="1month" />
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
<p className="flex items-baseline gap-1">
|
||||||
|
<span className="text-xl font-semibold">${monthlyPrice}</span>
|
||||||
|
<span className="text-muted-foreground text-sm">/ mo</span>
|
||||||
|
</p>
|
||||||
|
</li>
|
||||||
|
</RadioGroup>
|
||||||
</section>
|
</section>
|
||||||
<footer className="mt-4 grid gap-2.5">
|
<footer className="space-y-4">
|
||||||
<Button variant="primary" asChild>
|
<div className="grid gap-2">
|
||||||
<Link href="/settings/billing">
|
<UpgradeButton large annual={period === "yearly"}>
|
||||||
<Trans i18nKey="upgrade" defaults="Upgrade" />
|
<Trans i18nKey="subscribe" defaults="Subscribe" />
|
||||||
</Link>
|
</UpgradeButton>
|
||||||
</Button>
|
</div>
|
||||||
<DialogClose asChild>
|
<p className="text-muted-foreground text-center text-sm">
|
||||||
<Button variant="ghost">
|
<Trans
|
||||||
<Trans i18nKey="notToday" defaults="Not Today" />
|
i18nKey="cancelAnytime"
|
||||||
</Button>
|
defaults="Cancel anytime from your <a>billing page</a>."
|
||||||
</DialogClose>
|
components={{
|
||||||
|
a: <Link className="text-link" href="/settings/billing" />,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</p>
|
||||||
</footer>
|
</footer>
|
||||||
</article>
|
</div>
|
||||||
</DialogContent>
|
</DialogContent>
|
||||||
);
|
</Dialog>
|
||||||
}
|
);
|
||||||
return <>{children}</>;
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -27,11 +27,14 @@ import Link from "next/link";
|
||||||
import * as React from "react";
|
import * as React from "react";
|
||||||
|
|
||||||
import { DuplicateDialog } from "@/app/[locale]/poll/[urlId]/duplicate-dialog";
|
import { DuplicateDialog } from "@/app/[locale]/poll/[urlId]/duplicate-dialog";
|
||||||
|
import { PayWallDialogContent } from "@/app/[locale]/poll/[urlId]/pay-wall-dialog-content";
|
||||||
import { trpc } from "@/app/providers";
|
import { trpc } from "@/app/providers";
|
||||||
import { FinalizePollDialog } from "@/components/poll/manage-poll/finalize-poll-dialog";
|
import { FinalizePollDialog } from "@/components/poll/manage-poll/finalize-poll-dialog";
|
||||||
import { ProFeatureBadge } from "@/components/pro-feature-badge";
|
import { ProFeatureBadge } from "@/components/pro-feature-badge";
|
||||||
import { Trans } from "@/components/trans";
|
import { Trans } from "@/components/trans";
|
||||||
|
import { usePlan } from "@/contexts/plan";
|
||||||
import { usePoll } from "@/contexts/poll";
|
import { usePoll } from "@/contexts/poll";
|
||||||
|
import { usePostHog } from "@/utils/posthog";
|
||||||
|
|
||||||
import { DeletePollDialog } from "./manage-poll/delete-poll-dialog";
|
import { DeletePollDialog } from "./manage-poll/delete-poll-dialog";
|
||||||
import { useCsvExporter } from "./manage-poll/use-csv-exporter";
|
import { useCsvExporter } from "./manage-poll/use-csv-exporter";
|
||||||
|
@ -144,6 +147,9 @@ const ManagePoll: React.FunctionComponent<{
|
||||||
const [showDeletePollDialog, setShowDeletePollDialog] = React.useState(false);
|
const [showDeletePollDialog, setShowDeletePollDialog] = React.useState(false);
|
||||||
const duplicateDialog = useDialog();
|
const duplicateDialog = useDialog();
|
||||||
const finalizeDialog = useDialog();
|
const finalizeDialog = useDialog();
|
||||||
|
const paywallDialog = useDialog();
|
||||||
|
const plan = usePlan();
|
||||||
|
const posthog = usePostHog();
|
||||||
const { exportToCsv } = useCsvExporter();
|
const { exportToCsv } = useCsvExporter();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
@ -161,37 +167,6 @@ const ManagePoll: React.FunctionComponent<{
|
||||||
</Button>
|
</Button>
|
||||||
</DropdownMenuTrigger>
|
</DropdownMenuTrigger>
|
||||||
<DropdownMenuContent align="end">
|
<DropdownMenuContent align="end">
|
||||||
<>
|
|
||||||
{poll.status === "finalized" ? (
|
|
||||||
<DropdownMenuItem
|
|
||||||
onClick={() => {
|
|
||||||
reopen.mutate({ pollId: poll.id });
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<Icon>
|
|
||||||
<RotateCcwIcon />
|
|
||||||
</Icon>
|
|
||||||
<Trans i18nKey="reopenPoll" defaults="Reopen" />
|
|
||||||
</DropdownMenuItem>
|
|
||||||
) : (
|
|
||||||
<>
|
|
||||||
<DropdownMenuItem
|
|
||||||
disabled={!!poll.event}
|
|
||||||
onClick={() => {
|
|
||||||
finalizeDialog.trigger();
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<Icon>
|
|
||||||
<CalendarCheck2Icon />
|
|
||||||
</Icon>
|
|
||||||
<Trans i18nKey="finishPoll" defaults="Finalize" />
|
|
||||||
<ProFeatureBadge />
|
|
||||||
</DropdownMenuItem>
|
|
||||||
<PauseResumeToggle />
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
</>
|
|
||||||
<DropdownMenuSeparator />
|
|
||||||
<DropdownMenuItem asChild>
|
<DropdownMenuItem asChild>
|
||||||
<Link href={`/poll/${poll.id}/edit-details`}>
|
<Link href={`/poll/${poll.id}/edit-details`}>
|
||||||
<DropdownMenuItemIconLabel icon={PencilIcon}>
|
<DropdownMenuItemIconLabel icon={PencilIcon}>
|
||||||
|
@ -214,6 +189,44 @@ const ManagePoll: React.FunctionComponent<{
|
||||||
</Link>
|
</Link>
|
||||||
</DropdownMenuItem>
|
</DropdownMenuItem>
|
||||||
<DropdownMenuSeparator />
|
<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,
|
||||||
|
action: "finalize",
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
finalizeDialog.trigger();
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Icon>
|
||||||
|
<CalendarCheck2Icon />
|
||||||
|
</Icon>
|
||||||
|
<Trans i18nKey="finishPoll" defaults="Finalize" />
|
||||||
|
<ProFeatureBadge />
|
||||||
|
</DropdownMenuItem>
|
||||||
|
<PauseResumeToggle />
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
<DropdownMenuSeparator />
|
<DropdownMenuSeparator />
|
||||||
<DropdownMenuItem onClick={exportToCsv}>
|
<DropdownMenuItem onClick={exportToCsv}>
|
||||||
<DropdownMenuItemIconLabel icon={DownloadIcon}>
|
<DropdownMenuItemIconLabel icon={DownloadIcon}>
|
||||||
|
@ -222,7 +235,15 @@ const ManagePoll: React.FunctionComponent<{
|
||||||
</DropdownMenuItem>
|
</DropdownMenuItem>
|
||||||
<DropdownMenuItem
|
<DropdownMenuItem
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
duplicateDialog.trigger();
|
if (plan === "free") {
|
||||||
|
paywallDialog.trigger();
|
||||||
|
posthog?.capture("trigger paywall", {
|
||||||
|
poll_id: poll.id,
|
||||||
|
action: "duplicate",
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
duplicateDialog.trigger();
|
||||||
|
}
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<DropdownMenuItemIconLabel icon={CopyIcon}>
|
<DropdownMenuItemIconLabel icon={CopyIcon}>
|
||||||
|
@ -253,6 +274,7 @@ const ManagePoll: React.FunctionComponent<{
|
||||||
{...duplicateDialog.dialogProps}
|
{...duplicateDialog.dialogProps}
|
||||||
/>
|
/>
|
||||||
<FinalizePollDialog {...finalizeDialog.dialogProps} />
|
<FinalizePollDialog {...finalizeDialog.dialogProps} />
|
||||||
|
<PayWallDialogContent {...paywallDialog.dialogProps} />
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
@ -24,7 +24,6 @@ import React from "react";
|
||||||
import { useForm } from "react-hook-form";
|
import { useForm } from "react-hook-form";
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
|
|
||||||
import { PayWallDialogContent } from "@/app/[locale]/poll/[urlId]/pay-wall-dialog-content";
|
|
||||||
import { trpc } from "@/app/providers";
|
import { trpc } from "@/app/providers";
|
||||||
import { DateIconInner } from "@/components/date-icon";
|
import { DateIconInner } from "@/components/date-icon";
|
||||||
import { useParticipants } from "@/components/participants-provider";
|
import { useParticipants } from "@/components/participants-provider";
|
||||||
|
@ -206,61 +205,6 @@ export const FinalizePollForm = ({
|
||||||
);
|
);
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
{/* <FormField
|
|
||||||
control={form.control}
|
|
||||||
name="notify"
|
|
||||||
render={({ field }) => (
|
|
||||||
<FormItem>
|
|
||||||
<FormLabel htmlFor="notify" className="mb-4">
|
|
||||||
<Trans i18nKey="notify" defaults="Send a calendar invite to" />
|
|
||||||
</FormLabel>
|
|
||||||
<FormControl>
|
|
||||||
<RadioGroup onValueChange={field.onChange} value={field.value}>
|
|
||||||
<Label className="flex items-center gap-4 font-normal">
|
|
||||||
<RadioGroupItem value="all" />
|
|
||||||
<Trans
|
|
||||||
i18nKey="notifyAllParticipants"
|
|
||||||
defaults="Everyone"
|
|
||||||
/>
|
|
||||||
</Label>
|
|
||||||
<Label className="flex items-center gap-4 font-normal">
|
|
||||||
<RadioGroupItem value="attendees" />
|
|
||||||
<Trans
|
|
||||||
i18nKey="notifyAvailableParticipants"
|
|
||||||
defaults="Attendees"
|
|
||||||
/>
|
|
||||||
</Label>
|
|
||||||
<Label className="flex items-center gap-4 font-normal">
|
|
||||||
<RadioGroupItem value="none" />
|
|
||||||
<Trans i18nKey="notifyNo" defaults="None" />
|
|
||||||
</Label>
|
|
||||||
</RadioGroup>
|
|
||||||
</FormControl>
|
|
||||||
<FormDescription>
|
|
||||||
<Trans
|
|
||||||
i18nKey="notifyDescription"
|
|
||||||
defaults="Choose which participants should receive a calendar invite"
|
|
||||||
/>
|
|
||||||
</FormDescription>
|
|
||||||
{participantsWithoutEmails.length ? (
|
|
||||||
<Alert>
|
|
||||||
<AlertCircleIcon className="size-4" />
|
|
||||||
<AlertDescription>
|
|
||||||
<Trans
|
|
||||||
i18nKey="missingEmailsAlert"
|
|
||||||
defaults="The following participants have not provided an email address."
|
|
||||||
/>
|
|
||||||
</AlertDescription>
|
|
||||||
<AlertDescription>
|
|
||||||
{participantsWithoutEmails.map((participant) => (
|
|
||||||
<div key={participant.id}>{participant.name}</div>
|
|
||||||
))}
|
|
||||||
</AlertDescription>
|
|
||||||
</Alert>
|
|
||||||
) : null}
|
|
||||||
</FormItem>
|
|
||||||
)}
|
|
||||||
/> */}
|
|
||||||
</form>
|
</form>
|
||||||
</Form>
|
</Form>
|
||||||
);
|
);
|
||||||
|
@ -276,47 +220,45 @@ export function FinalizePollDialog(props: DialogProps) {
|
||||||
});
|
});
|
||||||
return (
|
return (
|
||||||
<Dialog {...props}>
|
<Dialog {...props}>
|
||||||
<PayWallDialogContent>
|
<DialogContent size="2xl">
|
||||||
<DialogContent size="2xl">
|
<DialogHeader>
|
||||||
<DialogHeader>
|
<DialogTitle>
|
||||||
<DialogTitle>
|
<Trans i18nKey="finalize" />
|
||||||
<Trans i18nKey="finalize" />
|
</DialogTitle>
|
||||||
</DialogTitle>
|
<DialogDescription>
|
||||||
<DialogDescription>
|
<Trans
|
||||||
<Trans
|
i18nKey="finalizeDescription"
|
||||||
i18nKey="finalizeDescription"
|
defaults="Select a final date for your event."
|
||||||
defaults="Select a final date for your event."
|
/>
|
||||||
/>
|
</DialogDescription>
|
||||||
</DialogDescription>
|
</DialogHeader>
|
||||||
</DialogHeader>
|
<FinalizePollForm
|
||||||
<FinalizePollForm
|
name="finalize-form"
|
||||||
name="finalize-form"
|
onSubmit={(data) => {
|
||||||
onSubmit={(data) => {
|
scheduleEvent.mutate({
|
||||||
scheduleEvent.mutate({
|
pollId: poll.id,
|
||||||
pollId: poll.id,
|
optionId: data.selectedOptionId,
|
||||||
optionId: data.selectedOptionId,
|
notify: data.notify,
|
||||||
notify: data.notify,
|
});
|
||||||
});
|
props.onOpenChange?.(false);
|
||||||
props.onOpenChange?.(false);
|
}}
|
||||||
}}
|
/>
|
||||||
/>
|
<DialogFooter>
|
||||||
<DialogFooter>
|
<DialogClose asChild>
|
||||||
<DialogClose asChild>
|
<Button>
|
||||||
<Button>
|
<Trans i18nKey="cancel" />
|
||||||
<Trans i18nKey="cancel" />
|
|
||||||
</Button>
|
|
||||||
</DialogClose>
|
|
||||||
<Button
|
|
||||||
loading={scheduleEvent.isLoading}
|
|
||||||
type="submit"
|
|
||||||
form="finalize-form"
|
|
||||||
variant="primary"
|
|
||||||
>
|
|
||||||
<Trans i18nKey="finalize" />
|
|
||||||
</Button>
|
</Button>
|
||||||
</DialogFooter>
|
</DialogClose>
|
||||||
</DialogContent>
|
<Button
|
||||||
</PayWallDialogContent>
|
loading={scheduleEvent.isLoading}
|
||||||
|
type="submit"
|
||||||
|
form="finalize-form"
|
||||||
|
variant="primary"
|
||||||
|
>
|
||||||
|
<Trans i18nKey="finalize" />
|
||||||
|
</Button>
|
||||||
|
</DialogFooter>
|
||||||
|
</DialogContent>
|
||||||
</Dialog>
|
</Dialog>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,7 +8,8 @@ import { usePostHog } from "@/utils/posthog";
|
||||||
export const UpgradeButton = ({
|
export const UpgradeButton = ({
|
||||||
children,
|
children,
|
||||||
annual,
|
annual,
|
||||||
}: React.PropsWithChildren<{ annual?: boolean }>) => {
|
large,
|
||||||
|
}: React.PropsWithChildren<{ annual?: boolean; large?: boolean }>) => {
|
||||||
const posthog = usePostHog();
|
const posthog = usePostHog();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
@ -24,6 +25,7 @@ export const UpgradeButton = ({
|
||||||
value={window.location.pathname}
|
value={window.location.pathname}
|
||||||
/>
|
/>
|
||||||
<Button
|
<Button
|
||||||
|
size={large ? "lg" : "default"}
|
||||||
className="w-full"
|
className="w-full"
|
||||||
type="submit"
|
type="submit"
|
||||||
variant="primary"
|
variant="primary"
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue