diff --git a/apps/web/public/locales/en/app.json b/apps/web/public/locales/en/app.json index 41a94c259..5f1a99b73 100644 --- a/apps/web/public/locales/en/app.json +++ b/apps/web/public/locales/en/app.json @@ -241,13 +241,6 @@ "dangerZoneAccount": "Delete your account permanently. This action cannot be undone.", "upgradePromptTitle": "Upgrade to Pro", "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 {email}", "home": "Home", "groupPoll": "Group Poll", @@ -282,5 +275,14 @@ "fileTooLarge": "File too large", "fileTooLargeDescription": "Please upload a file smaller than 2MB.", "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 billing page." } diff --git a/apps/web/src/app/[locale]/poll/[urlId]/pay-wall-dialog-content.tsx b/apps/web/src/app/[locale]/poll/[urlId]/pay-wall-dialog-content.tsx index cb9f54f63..8c7774466 100644 --- a/apps/web/src/app/[locale]/poll/[urlId]/pay-wall-dialog-content.tsx +++ b/apps/web/src/app/[locale]/poll/[urlId]/pay-wall-dialog-content.tsx @@ -1,33 +1,38 @@ +import { pricingData } from "@rallly/billing/pricing"; import { Badge } from "@rallly/ui/badge"; -import { Button } from "@rallly/ui/button"; -import { DialogClose, DialogContent } from "@rallly/ui/dialog"; +import { Dialog, DialogContent, DialogProps } from "@rallly/ui/dialog"; +import { RadioGroup, RadioGroupItem } from "@rallly/ui/radio-group"; import { m } from "framer-motion"; -import { - CalendarCheck2Icon, - ClockIcon, - CopyIcon, - Settings2Icon, -} from "lucide-react"; +import { CheckIcon } from "lucide-react"; import Link from "next/link"; +import { useState } from "react"; import { Trans } from "@/components/trans"; -import { usePlan } from "@/contexts/plan"; +import { UpgradeButton } from "@/components/upgrade-button"; -export function PayWallDialogContent({ - children, -}: { - children?: React.ReactNode; -}) { - const plan = usePlan(); +const annualSavingsPercentage = ( + ((pricingData.monthly.amount * 12 - pricingData.yearly.amount) / + (pricingData.monthly.amount * 12)) * + 100 +).toFixed(0); - if (plan === "free") { - return ( +const yearlyPrice = (pricingData.yearly.amount / 100).toFixed(2); +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 ( + -
-
+
+
-

+

-

+

-
-
-
    -
  • -
    -
    - -
    -
    -
    -

    - -

    -

    - -

    -
    +
      +
    • + +
    • -
    • -
      - -
      -
      -

      - -

      -

      - -

      -
      +
    • + +
    • -
    • -
      -
      - -
      -
      -
      -

      - -

      -

      - -

      -
      +
    • + +
    • -
    • -
      -
      - -
      -
      -
      -

      - -

      -

      - -

      -
      +
    • + +
    +
+
+ +
  • +
    +
    + + + + + +
    +

    + ${yearlyPrice} + + ${((pricingData.monthly.amount * 12) / 100).toFixed(2)} + +

    +
    +

    + + ${monthlyPriceAnnualRate} + + / mo +

    +
  • +
  • +
    + + +
    +

    + ${monthlyPrice} + / mo +

    +
  • +
    -
    +
    - ); - } - return <>{children}; +
    + ); } diff --git a/apps/web/src/components/poll/manage-poll.tsx b/apps/web/src/components/poll/manage-poll.tsx index 0f168505b..774cb9920 100644 --- a/apps/web/src/components/poll/manage-poll.tsx +++ b/apps/web/src/components/poll/manage-poll.tsx @@ -27,11 +27,14 @@ import Link from "next/link"; import * as React from "react"; 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 { 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 { usePostHog } from "@/utils/posthog"; import { DeletePollDialog } from "./manage-poll/delete-poll-dialog"; import { useCsvExporter } from "./manage-poll/use-csv-exporter"; @@ -144,6 +147,9 @@ const ManagePoll: React.FunctionComponent<{ 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 ( @@ -161,37 +167,6 @@ const ManagePoll: React.FunctionComponent<{ - <> - {poll.status === "finalized" ? ( - { - reopen.mutate({ pollId: poll.id }); - }} - > - - - - - - ) : ( - <> - { - finalizeDialog.trigger(); - }} - > - - - - - - - - - )} - - @@ -214,6 +189,44 @@ const ManagePoll: React.FunctionComponent<{ + <> + {poll.status === "finalized" ? ( + { + reopen.mutate({ pollId: poll.id }); + }} + > + + + + + + ) : ( + <> + { + if (plan === "free") { + paywallDialog.trigger(); + posthog?.capture("trigger paywall", { + poll_id: poll.id, + action: "finalize", + }); + } else { + finalizeDialog.trigger(); + } + }} + > + + + + + + + + + )} + @@ -222,7 +235,15 @@ const ManagePoll: React.FunctionComponent<{ { - duplicateDialog.trigger(); + if (plan === "free") { + paywallDialog.trigger(); + posthog?.capture("trigger paywall", { + poll_id: poll.id, + action: "duplicate", + }); + } else { + duplicateDialog.trigger(); + } }} > @@ -253,6 +274,7 @@ const ManagePoll: React.FunctionComponent<{ {...duplicateDialog.dialogProps} /> + ); }; diff --git a/apps/web/src/components/poll/manage-poll/finalize-poll-dialog.tsx b/apps/web/src/components/poll/manage-poll/finalize-poll-dialog.tsx index a0c9700ec..0fe198ddb 100644 --- a/apps/web/src/components/poll/manage-poll/finalize-poll-dialog.tsx +++ b/apps/web/src/components/poll/manage-poll/finalize-poll-dialog.tsx @@ -24,7 +24,6 @@ import React from "react"; import { useForm } from "react-hook-form"; import { z } from "zod"; -import { PayWallDialogContent } from "@/app/[locale]/poll/[urlId]/pay-wall-dialog-content"; import { trpc } from "@/app/providers"; import { DateIconInner } from "@/components/date-icon"; import { useParticipants } from "@/components/participants-provider"; @@ -206,61 +205,6 @@ export const FinalizePollForm = ({ ); }} /> - {/* ( - - - - - - - - - - - - - - - {participantsWithoutEmails.length ? ( - - - - - - - {participantsWithoutEmails.map((participant) => ( -
    {participant.name}
    - ))} -
    -
    - ) : null} -
    - )} - /> */} ); @@ -276,47 +220,45 @@ export function FinalizePollDialog(props: DialogProps) { }); return ( - - - - - - - - - - - { - scheduleEvent.mutate({ - pollId: poll.id, - optionId: data.selectedOptionId, - notify: data.notify, - }); - props.onOpenChange?.(false); - }} - /> - - - - - - - - + + + + ); } diff --git a/apps/web/src/components/upgrade-button.tsx b/apps/web/src/components/upgrade-button.tsx index f4073631d..c2217fddc 100644 --- a/apps/web/src/components/upgrade-button.tsx +++ b/apps/web/src/components/upgrade-button.tsx @@ -8,7 +8,8 @@ import { usePostHog } from "@/utils/posthog"; export const UpgradeButton = ({ children, annual, -}: React.PropsWithChildren<{ annual?: boolean }>) => { + large, +}: React.PropsWithChildren<{ annual?: boolean; large?: boolean }>) => { const posthog = usePostHog(); return ( @@ -24,6 +25,7 @@ export const UpgradeButton = ({ value={window.location.pathname} />