From 1dcdef2abbff3c79ef4f6cd0f9376f6d0afa9e41 Mon Sep 17 00:00:00 2001 From: 01zulfi <85733202+01zulfi@users.noreply.github.com> Date: Thu, 23 Jul 2026 11:51:23 +0500 Subject: [PATCH] web: redesign plans page Signed-off-by: 01zulfi <85733202+01zulfi@users.noreply.github.com> --- .../src/assets/featured/android-police.svg | 27 +- .../web/src/assets/featured/freedom-press.svg | 71 +- .../src/assets/featured/privacy-guides.svg | 13 +- apps/web/src/assets/featured/techlore.svg | 36 +- apps/web/src/assets/featured/theverge.svg | 4 +- apps/web/src/assets/featured/xda.svg | 31 +- apps/web/src/assets/star.svg | 3 + apps/web/src/components/accordion/index.tsx | 47 +- apps/web/src/components/icons/index.tsx | 4 + apps/web/src/dialogs/buy-dialog/plan-list.tsx | 1240 ++++++++++++----- apps/web/src/views/plans.tsx | 126 +- packages/icons/svgs/arrow-right.svg | 2 +- packages/icons/svgs/check.svg | 2 +- packages/icons/svgs/chevron-down.svg | 11 +- packages/icons/svgs/chevron-left.svg | 4 +- packages/icons/svgs/chevron-up.svg | 2 +- packages/icons/svgs/cloud.svg | 2 +- packages/icons/svgs/file.svg | 2 +- packages/icons/svgs/image-outline.svg | 2 +- packages/icons/svgs/image.svg | 2 +- packages/theme/src/theme/variants/button.ts | 13 + 21 files changed, 1185 insertions(+), 459 deletions(-) create mode 100644 apps/web/src/assets/star.svg diff --git a/apps/web/src/assets/featured/android-police.svg b/apps/web/src/assets/featured/android-police.svg index a3ae5f8ab..30e8c6d70 100644 --- a/apps/web/src/assets/featured/android-police.svg +++ b/apps/web/src/assets/featured/android-police.svg @@ -1,17 +1,12 @@ - - - - - - - - - - + + + + + - + + + + + + \ No newline at end of file diff --git a/apps/web/src/assets/featured/freedom-press.svg b/apps/web/src/assets/featured/freedom-press.svg index 90153beab..d80175c67 100644 --- a/apps/web/src/assets/featured/freedom-press.svg +++ b/apps/web/src/assets/featured/freedom-press.svg @@ -1,33 +1,40 @@ -
- )} - + ) : null} + {getCurrencySymbol(plan.currency)} - {monthlyPrice}{" "} - {isZero ? ( - "" - ) : ( - - / month - - )} + {monthlyPrice} - + {isZero ? ( + "" + ) : ( + + {" "} + / month + + )} + {isZero ? ( "forever" ) : plan.period === "monthly" ? ( @@ -479,11 +660,14 @@ function OneTimePricing(props: PricingProps) { const { plan } = props; return ( <> - + {getCurrencySymbol(plan.currency)} {plan.price.gross} - + {formatOneTimePeriod(plan.period)} @@ -492,131 +676,509 @@ function OneTimePricing(props: PricingProps) { const rows = getFeaturesTable(); export function ComparePlans() { + const plans = [ + SubscriptionPlan.FREE, + SubscriptionPlan.ESSENTIAL, + SubscriptionPlan.PRO, + SubscriptionPlan.BELIEVER + ]; + return ( - - Compare plans - - + + + + + - - + Compare Plans + + + Choose the plan that fits your workflow and unlock features for secure + note taking. Compare storage, syncing, privacy, and productivity + features to find the experience + + + + + - {[ - { id: "id", title: "", width: "5%" }, - ...[ - SubscriptionPlan.FREE, - SubscriptionPlan.ESSENTIAL, - SubscriptionPlan.PRO, - SubscriptionPlan.BELIEVER - ].map((p) => ({ - id: p, - title: PLAN_METADATA[p].title, - width: "20%" - })) - ].map((column) => - !column.title ? ( - - - {rows.map((feature) => ( - + + Features + + + {plans.map((plan) => ( + + {PLAN_METADATA[plan].title} + + + ))} + + {rows.map((feature, rowIndex) => ( + + + {feature[0]} - {feature.slice(1).map((limit, index) => ( - - - - ))} - - ))} - -
- ) : ( - - - {column.title} - - - ) - )} - -
+
+ {feature.slice(1).map((limit, index) => ( + + {typeof (limit as any).caption === "boolean" ? ( + (limit as any).caption ? ( + + ) : ( + + - + + ) + ) : (limit as any).caption === "infinity" ? ( + + ∞ + + ) : ( + + {(limit as any).caption} + + )} + + ))} + + ))} + ); } -function Testimonial() { +export function TestimonialsCarousel() { + const [currentIndex, setCurrentIndex] = useState(0); + const [maxOffset, setMaxOffset] = useState(0); + const viewportRef = useRef(null); + const trackRef = useRef(null); + + useEffect(() => { + const updateMaxOffset = () => { + const viewport = viewportRef.current; + const track = trackRef.current; + if (!viewport || !track) return; + + setMaxOffset(Math.max(0, track.scrollWidth - viewport.clientWidth)); + }; + + updateMaxOffset(); + + const resizeObserver = new ResizeObserver(updateMaxOffset); + if (viewportRef.current) resizeObserver.observe(viewportRef.current); + if (trackRef.current) resizeObserver.observe(trackRef.current); + + return () => resizeObserver.disconnect(); + }, []); + + const lastIndex = Math.ceil(maxOffset / 620); + const offset = Math.min(currentIndex * 620, maxOffset); + + useEffect(() => { + setCurrentIndex((index) => Math.min(index, lastIndex)); + }, [lastIndex]); + + const isFirst = currentIndex === 0; + const isLast = currentIndex >= lastIndex; + + const goToPrev = () => { + if (isFirst) return; + setCurrentIndex((prev) => prev - 1); + }; + + const goToNext = () => { + if (isLast) return; + setCurrentIndex((prev) => prev + 1); + }; + return ( - - + + - {testimonial.text} —{" "} - - source - - - - - - - {testimonial.name} - - @{testimonial.username} + Testimonials +
+ + See how people use Notesnook to organize ideas, manage daily work, and + stay focused with a calm and secure writing experience. + + + + + {testimonials.map((t, index) => ( + + + + + + + + + + {t.name} + + + {t.role} + + + + + {t.text} + + + + ))} + + + + + + + + + + diff --git a/apps/web/src/views/plans.tsx b/apps/web/src/views/plans.tsx index e688b9365..3827700f2 100644 --- a/apps/web/src/views/plans.tsx +++ b/apps/web/src/views/plans.tsx @@ -20,16 +20,19 @@ along with this program. If not, see . import { SubscriptionPlan } from "@notesnook/core"; import { ComparePlans, + FeaturedOn, Footer, - PlansList + PlansList, + TestimonialsCarousel } from "../dialogs/buy-dialog/plan-list"; import { FlexScrollContainer } from "../components/scroll-container"; -import { Button, Flex, Text } from "@theme-ui/components"; +import { Flex, Text, Button, Image, Box } from "@notesnook/ui"; import { useStore as useUserStore } from "../stores/user-store"; import { useEffect } from "react"; import { getQueryParams, hardNavigate } from "../navigation"; -import { Close } from "../components/icons"; import { isUserSubscribed } from "../hooks/use-is-user-premium"; +import { ChevronLeft } from "../components/icons"; +import Star from "../assets/star.svg"; function Plans() { const user = useUserStore((store) => store.user); @@ -46,56 +49,112 @@ function Plans() { return ( - - + + + + + + - Select a plan + Notesnook Plans - One subscription for a lifetime of notes. + Choose a plan that fits your workflow and keep your notes secure + across every device. +
+ Enjoy powerful privacy-focused features designed for + distraction-free writing and organization.
+ +