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 e91f8957a..7c9cee56c 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,36 +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 { Label } from "@rallly/ui/label"; +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 +

    +
  • +
  • -

    - ${pricingData.monthly.amount / 100} per month -

    -
  • -
  • -
    - - -
    -

    - ${pricingData.yearly.amount / 100} per month +

    + ${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..806fe5220 100644 --- a/apps/web/src/components/poll/manage-poll.tsx +++ b/apps/web/src/components/poll/manage-poll.tsx @@ -27,10 +27,12 @@ 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 { DeletePollDialog } from "./manage-poll/delete-poll-dialog"; @@ -144,6 +146,8 @@ const ManagePoll: React.FunctionComponent<{ const [showDeletePollDialog, setShowDeletePollDialog] = React.useState(false); const duplicateDialog = useDialog(); const finalizeDialog = useDialog(); + const paywallDialog = useDialog(); + const plan = usePlan(); const { exportToCsv } = useCsvExporter(); return ( @@ -161,37 +165,6 @@ const ManagePoll: React.FunctionComponent<{ - <> - {poll.status === "finalized" ? ( - { - reopen.mutate({ pollId: poll.id }); - }} - > - - - - - - ) : ( - <> - { - finalizeDialog.trigger(); - }} - > - - - - - - - - - )} - - @@ -214,6 +187,40 @@ const ManagePoll: React.FunctionComponent<{ + <> + {poll.status === "finalized" ? ( + { + reopen.mutate({ pollId: poll.id }); + }} + > + + + + + + ) : ( + <> + { + if (plan === "free") { + paywallDialog.trigger(); + } else { + finalizeDialog.trigger(); + } + }} + > + + + + + + + + + )} + @@ -222,7 +229,11 @@ const ManagePoll: React.FunctionComponent<{ { - duplicateDialog.trigger(); + if (plan === "free") { + paywallDialog.trigger(); + } else { + duplicateDialog.trigger(); + } }} > @@ -253,6 +264,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..609ea8852 100644 --- a/apps/web/src/components/upgrade-button.tsx +++ b/apps/web/src/components/upgrade-button.tsx @@ -24,6 +24,7 @@ export const UpgradeButton = ({ value={window.location.pathname} />