From a773cd98c214d029ec0dbe06893de88451c5c881 Mon Sep 17 00:00:00 2001 From: Skylar Date: Tue, 10 Sep 2024 09:16:39 -0700 Subject: [PATCH] feat: calculator fixups and polish (#200) This PR incorporates various fixes and improvements for the calculator page based on feedback. - Calculator numbers update live as inputs change - Homepage calculator widget shows placeholder / blank numbers once inputs are changed from default values (so the user clicks "calculate" to see new numbers) - Calculator validates inputs from url parameters to ensure they are in range - Calculator page has its own more minimal demo CTA component, different from the homepage --- website/app/calculator/page.tsx | 21 +---- website/components/CTAButtons.tsx | 3 +- website/components/CTADemo.tsx | 76 +++++++++++++++++++ .../Calculator/CalculatorInputs.tsx | 22 +----- website/components/Calculator/index.tsx | 41 +++------- website/components/SavingsGraph.tsx | 13 ++-- website/components/SavingsSection.tsx | 24 ++++-- website/constants/urls.ts | 2 + website/context/CalcualtorContext.tsx | 6 +- 9 files changed, 122 insertions(+), 86 deletions(-) create mode 100644 website/components/CTADemo.tsx create mode 100644 website/constants/urls.ts diff --git a/website/app/calculator/page.tsx b/website/app/calculator/page.tsx index 40ae3f3c..f6210140 100644 --- a/website/app/calculator/page.tsx +++ b/website/app/calculator/page.tsx @@ -1,9 +1,8 @@ import { Metadata } from "next"; -import { ButtonPrimary } from "@/components/Button"; import Calculator from "@/components/Calculator"; import CTA from "@/components/CTA"; -import CTASmall from "@/components/CTASmall"; +import CTADemo from "@/components/CTADemo"; import SavingsGraph from "@/components/SavingsGraph"; import Spacer from "@/components/Spacer"; import { TextBase } from "@/components/Text"; @@ -17,7 +16,7 @@ const Page = () => { return ( <> { - - - Use the link below to book
a personalized demo of - Kardinal. -
- - Get a Demo - -
+ ); }; diff --git a/website/components/CTAButtons.tsx b/website/components/CTAButtons.tsx index b505f9e7..0361c77d 100644 --- a/website/components/CTAButtons.tsx +++ b/website/components/CTAButtons.tsx @@ -4,13 +4,14 @@ import styled from "styled-components"; import { ButtonPrimary, ButtonTertiary } from "@/components/Button"; import { mobile } from "@/constants/breakpoints"; +import { calendlyDemoUrl } from "@/constants/urls"; const CTAButtons = () => { return ( } diff --git a/website/components/CTADemo.tsx b/website/components/CTADemo.tsx new file mode 100644 index 00000000..24a994d3 --- /dev/null +++ b/website/components/CTADemo.tsx @@ -0,0 +1,76 @@ +"use client"; + +import { FiArrowRight } from "react-icons/fi"; +import styled from "styled-components"; + +import Button from "@/components/Button"; +import Heading from "@/components/Heading"; +import Section from "@/components/Section"; +import Text from "@/components/Text"; +import { mobile } from "@/constants/breakpoints"; +import { calendlyDemoUrl } from "@/constants/urls"; + +const CTADemo = () => { + return ( +
+ + + + Fancy a demo? + + Schedule some time for a personalized demo of Kardinal. + + + } + href={calendlyDemoUrl} + rel="noopener noreferrer" + target="_blank" + > + Schedule a demo + + + +
+ ); +}; + +const S = { + CTADemo: styled.div` + display: flex; + width: 100%; + padding: 64px 0; + align-items: center; + justify-content: center; + `, + + Content: styled.div` + max-width: 1038px; + border-radius: 12px; + background-color: rgba(252, 160, 97, 0.08); + width: 100%; + display: flex; + justify-content: space-between; + align-items: center; + padding: 48px; + gap: 4px; + + @media ${mobile} { + flex-direction: column; + gap: 16px; + align-items: flex-start; + } + `, + + TextWrapper: styled.div` + display: flex; + flex-direction: column; + gap: 4px; + @media ${mobile} { + gap: 8px; + } + `, +}; + +export default CTADemo; diff --git a/website/components/Calculator/CalculatorInputs.tsx b/website/components/Calculator/CalculatorInputs.tsx index af3e67e5..7b6952e9 100644 --- a/website/components/Calculator/CalculatorInputs.tsx +++ b/website/components/Calculator/CalculatorInputs.tsx @@ -11,10 +11,6 @@ import { useCalculatorContext, } from "@/context/CalcualtorContext"; -interface Props { - onCalculate: () => void; -} - const resourceRequirementsOptions: ResourceRequirement[] = [ ResourceRequirement.MICRO, ResourceRequirement.SMALL, @@ -23,7 +19,7 @@ const resourceRequirementsOptions: ResourceRequirement[] = [ const costIntervalOptions: CostInterval[] = ["Year", "Month"]; -const CostSavingsCalculator = ({ onCalculate }: Props) => { +const CostSavingsCalculator = () => { const { engineers, setEngineers, @@ -98,16 +94,6 @@ const CostSavingsCalculator = ({ onCalculate }: Props) => { - - - } - onClick={onCalculate} - > - Calculate! - - ); }; @@ -242,12 +228,6 @@ namespace S { top: calc(50% + 4px); color: var(--gray-400); `; - - export const ButtonWrapper = styled.div` - display: flex; - align-items: center; - justify-content: flex-end; - `; } export default CostSavingsCalculator; diff --git a/website/components/Calculator/index.tsx b/website/components/Calculator/index.tsx index 7bd3ebfc..a06c8d54 100644 --- a/website/components/Calculator/index.tsx +++ b/website/components/Calculator/index.tsx @@ -45,45 +45,24 @@ const Calculator = () => { const costPerServiceHour = HOURLY_COST_PER_RESOURCE_REQUIREMENT[resourceRequirement]; - const calculateCostBefore = () => - microservices * engineers * costPerServiceHour; - const calculateCostAfter = () => - (microservices + engineers) * costPerServiceHour; - const calculateSavings = () => calculateCostBefore() - calculateCostAfter(); - - // Use copies of state values so numbers only change when calculate button is clicked - const [costBefore, setCostBefore] = useState(calculateCostBefore()); - const [costAfter, setCostAfter] = useState(calculateCostAfter()); - const [savings, setSavings] = useState(calculateSavings()); - const [interval, setInterval] = useState(costInterval); - - // only update values when user clicks calculate - const handleCalculate = () => { - setCostBefore(calculateCostBefore()); - setCostAfter(calculateCostAfter()); - setSavings(calculateSavings()); - setInterval(costInterval); - - analytics.track("CALCULATE", { - numEngineers: engineers, - numServices: microservices, - }); - }; + const costBefore = microservices * engineers * costPerServiceHour; + const costAfter = (microservices + engineers) * costPerServiceHour; + const savings = costBefore - costAfter; return (
{"put in your organization numbers to see cost savings 👇🏻"} - + { title="Your costs after" values={[ { - label: `Services cost after (per ${interval.toLowerCase()})`, + label: `Services cost after (per ${costInterval.toLowerCase()})`, value: currencyFormatter.format( - costAfter * WORKING_HOURS_PER_COST_INTERVAL[interval], + costAfter * WORKING_HOURS_PER_COST_INTERVAL[costInterval], ), }, { @@ -116,9 +95,9 @@ const Calculator = () => { value: 100 - Math.round((costAfter / costBefore) * 100) + "%", }, { - label: `Cost savings per ${interval.toLowerCase()}*`, + label: `Cost savings per ${costInterval.toLowerCase()}*`, value: currencyFormatter.format( - savings * WORKING_HOURS_PER_COST_INTERVAL[interval], + savings * WORKING_HOURS_PER_COST_INTERVAL[costInterval], ), }, ]} diff --git a/website/components/SavingsGraph.tsx b/website/components/SavingsGraph.tsx index 84b40424..49d7183e 100644 --- a/website/components/SavingsGraph.tsx +++ b/website/components/SavingsGraph.tsx @@ -13,7 +13,7 @@ import savingsGraphImg from "@/public/illustrations/savings-graph.svg"; const SavingsGraph = () => { return ( -
+
{ + + * Graph values are approximate. Based on use case with 20 + microservices. + - - * Graph values are approximate. Based on use case with 20 microservices. -
); }; @@ -52,9 +53,11 @@ namespace S { display: grid; grid-template-columns: 1fr 1fr; grid-gap: 64px; - margin-bottom: 24px; + padding: 100px 0; + @media ${tablet} { grid-template-columns: 1fr; + padding: 64px 0; } `; diff --git a/website/components/SavingsSection.tsx b/website/components/SavingsSection.tsx index b640a697..62979837 100644 --- a/website/components/SavingsSection.tsx +++ b/website/components/SavingsSection.tsx @@ -10,9 +10,15 @@ import Text from "@/components/Text"; import { tablet } from "@/constants/breakpoints"; import analytics from "@/lib/analytics"; +const INITIAL_ENGINEERS = 20; +const INITIAL_MICROSERVICES = 60; + const SavingsSection = () => { - const [engineers, setEngineers] = useState(20); - const [microservices, setMicroservices] = useState(60); + const [engineers, setEngineers] = useState(INITIAL_ENGINEERS); + const [microservices, setMicroservices] = useState(INITIAL_MICROSERVICES); + + const inputsAreInitialValues = + engineers === INITIAL_ENGINEERS && microservices === INITIAL_MICROSERVICES; return (
@@ -66,11 +72,15 @@ const SavingsSection = () => { Potential savings: - {"$26,726.40"} - - - ~93% - + + {inputsAreInitialValues ? "$24,944.64" : "$--,---.--"} + + {inputsAreInitialValues && ( + + + ~93% + + )} diff --git a/website/constants/urls.ts b/website/constants/urls.ts new file mode 100644 index 00000000..05ad0c8a --- /dev/null +++ b/website/constants/urls.ts @@ -0,0 +1,2 @@ +export const calendlyDemoUrl = + "https://calendly.com/d/cqhd-tgj-vmc/45-minute-meeting"; diff --git a/website/context/CalcualtorContext.tsx b/website/context/CalcualtorContext.tsx index 219202df..b3b2f8e0 100644 --- a/website/context/CalcualtorContext.tsx +++ b/website/context/CalcualtorContext.tsx @@ -43,10 +43,10 @@ export const CalculatorProvider = ({ children }: PropsWithChildren) => { : 60; const [engineers, setEngineers] = useState( - Math.min(initialEngineers, 100), - ); // max value 100 + Math.min(Math.max(initialEngineers, 2), 100), + ); // value from 2 to 100 const [microservices, setMicroservices] = useState( - Math.min(initialMicroservices, 100), + Math.min(Math.max(initialMicroservices, 2), 100), ); // max value 100 const [resourceRequirement, setResourceRequirement] = useState(ResourceRequirement.MICRO);