From 9657cba4e66649a4d11ac6522b99aa87fea83f8f Mon Sep 17 00:00:00 2001 From: Walter Galvao Date: Wed, 8 Jan 2025 01:45:55 -0300 Subject: [PATCH] feat: mkt pricing calculator + shadcn (#41) * feat(mkt): shadcn slider * fix(mkt): styles * feat(mkt): pricing calculator * fix(mkt): tooltips --- apps/mkt/app/(default)/page.tsx | 4 +- apps/mkt/app/css/style.css | 123 ++++++- apps/mkt/components.json | 21 ++ apps/mkt/components/pricing-cards.tsx | 241 ++++++++++++ apps/mkt/components/pricing-section.tsx | 161 ++++++++ apps/mkt/components/pricing-tabs.tsx | 365 ------------------- apps/mkt/components/tooltip.tsx | 3 +- apps/mkt/components/ui/button-contact-us.tsx | 2 +- apps/mkt/components/ui/slider.tsx | 28 ++ apps/mkt/lib/utils.ts | 6 + apps/mkt/package.json | 8 +- apps/mkt/tailwind.config.js | 290 ++++++++++----- package-lock.json | 304 ++++++++++++++- 13 files changed, 1080 insertions(+), 476 deletions(-) create mode 100644 apps/mkt/components.json create mode 100644 apps/mkt/components/pricing-cards.tsx create mode 100644 apps/mkt/components/pricing-section.tsx delete mode 100644 apps/mkt/components/pricing-tabs.tsx create mode 100644 apps/mkt/components/ui/slider.tsx create mode 100644 apps/mkt/lib/utils.ts diff --git a/apps/mkt/app/(default)/page.tsx b/apps/mkt/app/(default)/page.tsx index d5e50ea..43e8c23 100644 --- a/apps/mkt/app/(default)/page.tsx +++ b/apps/mkt/app/(default)/page.tsx @@ -3,9 +3,9 @@ import Cta from "@/components/cta"; import FeaturesOverview from "@/components/features-overview"; import FeaturesTraits from "@/components/features-traits"; import FeaturesAutomations from "@/components/features-automations"; -import PricingTabs from "@/components/pricing-tabs"; import ProblemStatements from "@/components/problem-statements"; import FeaturesSlack from "@/components/features-slack-digests"; +import PricingSection from "@/components/pricing-section"; export default function Home() { return ( @@ -16,7 +16,7 @@ export default function Home() { - + {/* */} diff --git a/apps/mkt/app/css/style.css b/apps/mkt/app/css/style.css index 96992cc..0d8d3d6 100644 --- a/apps/mkt/app/css/style.css +++ b/apps/mkt/app/css/style.css @@ -1,8 +1,119 @@ -@import 'tailwindcss/base'; -@import 'tailwindcss/components'; - /* Additional styles */ -@import 'additional-styles/utility-patterns.css'; -@import 'additional-styles/theme.css'; +@import "additional-styles/utility-patterns.css"; +@import "additional-styles/theme.css"; + +@tailwind base; +@tailwind components; +@tailwind utilities; + +@layer base { + :root { + --background: 0 0% 100%; + + --foreground: 240 10% 3.9%; + + --card: 0 0% 100%; + + --card-foreground: 240 10% 3.9%; + + --popover: 0 0% 100%; + + --popover-foreground: 240 10% 3.9%; + + --primary: 240 5.9% 10%; + + --primary-foreground: 0 0% 98%; + + --secondary: 240 4.8% 95.9%; + + --secondary-foreground: 240 5.9% 10%; + + --muted: 240 4.8% 95.9%; + + --muted-foreground: 240 3.8% 46.1%; + + --accent: 240 4.8% 95.9%; + + --accent-foreground: 240 5.9% 10%; + + --destructive: 0 84.2% 60.2%; + + --destructive-foreground: 0 0% 98%; + + --border: 240 5.9% 90%; + + --input: 240 5.9% 90%; + + --ring: 240 10% 3.9%; + + --chart-1: 12 76% 61%; + + --chart-2: 173 58% 39%; + + --chart-3: 197 37% 24%; + + --chart-4: 43 74% 66%; + + --chart-5: 27 87% 67%; + + --radius: 0.5rem; + } + .dark { + --background: 240 10% 3.9%; + + --foreground: 0 0% 98%; + + --card: 240 10% 3.9%; + + --card-foreground: 0 0% 98%; + + --popover: 240 10% 3.9%; + + --popover-foreground: 0 0% 98%; + + --primary: 0 0% 98%; + + --primary-foreground: 240 5.9% 10%; + + --secondary: 240 3.7% 15.9%; + + --secondary-foreground: 0 0% 98%; + + --muted: 240 3.7% 15.9%; + + --muted-foreground: 240 5% 64.9%; + + --accent: 240 3.7% 15.9%; + + --accent-foreground: 0 0% 98%; + + --destructive: 0 62.8% 30.6%; + + --destructive-foreground: 0 0% 98%; + + --border: 240 3.7% 15.9%; + + --input: 240 3.7% 15.9%; + + --ring: 240 4.9% 83.9%; + + --chart-1: 220 70% 50%; + + --chart-2: 160 60% 45%; + + --chart-3: 30 80% 55%; + + --chart-4: 280 65% 60%; + + --chart-5: 340 75% 55%; + } +} -@import 'tailwindcss/utilities'; +@layer base { + * { + @apply border-border; + } + body { + @apply bg-background text-foreground; + } +} diff --git a/apps/mkt/components.json b/apps/mkt/components.json new file mode 100644 index 0000000..191272d --- /dev/null +++ b/apps/mkt/components.json @@ -0,0 +1,21 @@ +{ + "$schema": "https://ui.shadcn.com/schema.json", + "style": "default", + "rsc": true, + "tsx": true, + "tailwind": { + "config": "tailwind.config.js", + "css": "app/css/style.css", + "baseColor": "zinc", + "cssVariables": true, + "prefix": "" + }, + "aliases": { + "components": "@/components", + "utils": "@/lib/utils", + "ui": "@/components/ui", + "lib": "@/lib", + "hooks": "@/hooks" + }, + "iconLibrary": "lucide" +} \ No newline at end of file diff --git a/apps/mkt/components/pricing-cards.tsx b/apps/mkt/components/pricing-cards.tsx new file mode 100644 index 0000000..311a0e1 --- /dev/null +++ b/apps/mkt/components/pricing-cards.tsx @@ -0,0 +1,241 @@ +"use client"; + +import { IconBrandGithub } from "@tabler/icons-react"; +import Tooltip from "./tooltip"; +import { ButtonContactUs } from "./ui/button-contact-us"; +import { Slider } from "./ui/slider"; +import { useState } from "react"; +import { Switch, Field, Label } from "@headlessui/react"; + +export const PricingCards = () => { + const [contributors, setContributors] = useState(10); + const [isYearly, setIsYearly] = useState(false); + const basePrice = 49; + const discount = isYearly ? 0.8 : 1; + const pricePerExtraContributor = 7 * discount; + const discountedPrice = Math.floor(basePrice * discount); + const extraContributors = Math.max(contributors - 5, 0); + const totalPrice = + discountedPrice + extraContributors * pricePerExtraContributor; + + return ( +
+ + + + + + + + +
+ {/* Pricing tab `1` */} +
+
+
+
+ Cloud +
+
+
+ ${totalPrice.toFixed(2)} / month{" "} +
+ {isYearly && ( + + billed yearly + + )} +
+
+ For organizations that move fast. +
+
+
+
+ + {contributors} contributors + + setContributors(value[0])} + /> +
+
    +
  • + + + + + All features + +
  • +
  • + + + + + 1 year data retention + +
  • +
  • + + + + + Regular support + +
  • +
+
+ +
+
+ + {/* Pricing tab 3 */} +
+
+
+
+ Enterprise +
+
+ + Contact us + +
+
+ For organizations operating at large-scale. +
+
+
+
    +
  • + + + + + All features + +
  • +
  • + + + + + Unlimited contributors + +
  • +
  • + + + + + On-premise supported + +
  • +
  • + + + + + 3+ years data retention + +
  • +
  • + + + + + + Priority support + +
  • +
+
+
+ +
+
+
+
+
+ ); +}; diff --git a/apps/mkt/components/pricing-section.tsx b/apps/mkt/components/pricing-section.tsx new file mode 100644 index 0000000..1bfc95b --- /dev/null +++ b/apps/mkt/components/pricing-section.tsx @@ -0,0 +1,161 @@ +import Accordion from "@/components/accordion"; +import Link from "next/link"; +import { PricingCards } from "./pricing-cards"; + +export default function PricingSection() { + const faqs = [ + { + title: "What is a contributor?", + text: ( + <> + A contributor is anyone who has created or reviewed a Pull Request in + the last 30 days. + + ), + active: true, + }, + { + title: "Is my repository code safe?", + text: ( + <> + Absolutely. Sweetr does not have access to your code. We follow + stringent security best practices to protect our servers and + application.{" "} + + Read more + + . + + ), + active: true, + }, + { + title: "Is self-hosting available?", + text: ( + <> + Yes! Self-hosting is free, forever. However, it comes with maintenance + and setup hurdles. We recommend the Cloud version unless you have hard + requirements to manage your own infrastructure.{" "} + + Learn how to self-host + + . + + ), + active: false, + }, + { + title: "Is training needed?", + text: "Not at all, you and your team should be able to use Sweetr without any prior training. We design simple and intuitive interfaces, and documentation is highly available.", + active: false, + }, + { + title: "How is my data used? Can it be removed?", + text: ( + <> + Sweetr only saves data necessary to provide service to you. You can + delete all of your private data from our servers by revoking OAuth + access or uninstalling our application from your GitHub account or + organization at any moment. + + Read our privacy policy. + + + ), + active: false, + }, + { + title: "Is GitLab or BitBucket supported?", + text: ( + <> + Not yet. They are in our backlog, you can check their status on + Featurebase. +
+ + BitBucket + {" "} + •{" "} + + {" "} + GitLab + +
+ + ), + active: false, + }, + { + title: "Are contributions accepted?", + text: ( + <> + Fore sure! We welcome PRs from the community. You can learn more in + our{" "} + + contribution guide + + . + + ), + active: false, + }, + ]; + + return ( +
+
+
+
+

+ Pricing +

+

+ No credit card required to get started. Self-host for free. +

+
+ + + + {/* FAQs */} +
+
+ {faqs.map((faq, index) => ( + + {faq.text} + + ))} +
+
+
+
+
+ ); +} diff --git a/apps/mkt/components/pricing-tabs.tsx b/apps/mkt/components/pricing-tabs.tsx deleted file mode 100644 index bb8e95e..0000000 --- a/apps/mkt/components/pricing-tabs.tsx +++ /dev/null @@ -1,365 +0,0 @@ -import Tooltip from "@/components/tooltip"; -import Accordion from "@/components/accordion"; -import Link from "next/link"; -import { IconBrandGithub, IconMessage } from "@tabler/icons-react"; -import { Crisp } from "crisp-sdk-web"; -import { ButtonContactUs } from "./ui/button-contact-us"; - -export default function PricingTabs() { - const faqs = [ - { - title: "What is a contributor?", - text: ( - <> - A contributor is anyone who has created or reviewed a Pull Request in - the last 30 days. - - ), - active: true, - }, - { - title: "Is my repository code safe?", - text: ( - <> - Absolutely. Sweetr does not have access to your code. We follow - stringent security best practices to protect our servers and - application.{" "} - - Read more - - . - - ), - active: true, - }, - { - title: "Is self-hosting available?", - text: ( - <> - Yes! Self-hosting is free, forever. However, it comes with maintenance - and setup hurdles. We recommend the Cloud version unless you have hard - requirements to manage your own infrastructure.{" "} - - Learn how to self-host - - . - - ), - active: false, - }, - { - title: "Is training needed?", - text: "Not at all, you and your team should be able to use Sweetr without any prior training. We design simple and intuitive interfaces, and documentation is highly available.", - active: false, - }, - { - title: "How is my data used? Can it be removed?", - text: ( - <> - Sweetr only saves data necessary to provide service to you. You can - delete all of your private data from our servers by revoking OAuth - access or uninstalling our application from your GitHub account or - organization at any moment. - - Read our privacy policy. - - - ), - active: false, - }, - { - title: "Is GitLab or BitBucket supported?", - text: ( - <> - Not yet. They are in our backlog, you can check their status on - Featurebase. -
- - BitBucket - {" "} - •{" "} - - {" "} - GitLab - -
- - ), - active: false, - }, - { - title: "Are contributions accepted?", - text: ( - <> - Fore sure! We welcome PRs from the community. You can learn more in - our{" "} - - contribution guide - - . - - ), - active: false, - }, - ]; - - return ( -
-
-
-
-

- Pricing -

-

- No credit card required to get started. Self-host for free. -

-
- - {/* Pricing tabs component */} -
-
- {/* Pricing tab `1` */} -
-
-
-
- Cloud -
-
-
- Starts at $49/mo -
-
-
- For organizations that move fast. -
-
-
-
- Details: -
-
    -
  • - - - - 5 contributors -
  • -
  • - - - - $7 per extra contributor -
  • -
  • - - - - 20% discount on yearly plan -
  • -
  • - - - - - 1 year data retention - -
  • -
  • - - - - - All features - -
  • -
-
- -
-
- - {/* Pricing tab 3 */} -
-
-
-
- Enterprise -
-
- - Contact us - -
-
- For organizations operating at large-scale. -
-
-
-
- Details: -
-
    -
  • - - - - Unlimited contributors -
  • -
  • - - - - - On-premise supported - -
  • -
  • - - - - - Dedicated support - -
  • -
  • - - - - - 3+ year data retention - -
  • -
  • - - - - - All features - -
  • -
-
-
- -
-
-
-
-
- - {/* FAQs */} -
-
- {faqs.map((faq, index) => ( - - {faq.text} - - ))} -
-
-
-
-
- ); -} diff --git a/apps/mkt/components/tooltip.tsx b/apps/mkt/components/tooltip.tsx index aa06bed..3b27290 100644 --- a/apps/mkt/components/tooltip.tsx +++ b/apps/mkt/components/tooltip.tsx @@ -40,7 +40,8 @@ export default function Tooltip({ > { return (