update public web
This commit is contained in:
@@ -34,39 +34,36 @@ const images = [
|
||||
{ title: 'The classic pie chart', url: '/demo/pie-min.png' },
|
||||
];
|
||||
|
||||
export function HomeCarousel() {
|
||||
export function PreviewCarousel() {
|
||||
return (
|
||||
<div className="mx-auto max-w-6xl p-4">
|
||||
<div className="relative">
|
||||
<div className="rounded-lg w-full max-w-6xl aspect-video dashed absolute -left-5 -top-5"></div>
|
||||
<Carousel
|
||||
className="w-full"
|
||||
opts={{ loop: true }}
|
||||
plugins={[
|
||||
Autoplay({
|
||||
delay: 2000,
|
||||
}),
|
||||
]}
|
||||
>
|
||||
<CarouselContent>
|
||||
{images.map((item) => (
|
||||
<CarouselItem key={item.url}>
|
||||
<div className="aspect-video rounded-md overflow-hidden">
|
||||
<Image
|
||||
className="w-full h-full object-cover"
|
||||
src={item.url}
|
||||
width={1080}
|
||||
height={608}
|
||||
alt={item.title}
|
||||
/>
|
||||
</div>
|
||||
</CarouselItem>
|
||||
))}
|
||||
</CarouselContent>
|
||||
<CarouselPrevious className="hidden md:visible" />
|
||||
<CarouselNext className="hidden md:visible" />
|
||||
</Carousel>
|
||||
</div>
|
||||
</div>
|
||||
<Carousel
|
||||
className="w-full"
|
||||
opts={{ loop: true }}
|
||||
plugins={[
|
||||
Autoplay({
|
||||
delay: 2000,
|
||||
}),
|
||||
]}
|
||||
>
|
||||
<CarouselContent>
|
||||
{images.map((item) => (
|
||||
<CarouselItem key={item.url} className="flex-[0_0_80%] pl-8">
|
||||
<div className="aspect-video">
|
||||
<div className="p-3 bg-white/20 rounded-xl overflow-hidden">
|
||||
<Image
|
||||
className="w-full h-full object-cover rounded-lg"
|
||||
src={item.url}
|
||||
width={1080}
|
||||
height={608}
|
||||
alt={item.title}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</CarouselItem>
|
||||
))}
|
||||
</CarouselContent>
|
||||
<CarouselPrevious className="hidden md:visible" />
|
||||
<CarouselNext className="hidden md:visible" />
|
||||
</Carousel>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -25,7 +25,9 @@ export function Paragraph({ children, className }: Props) {
|
||||
|
||||
export function Heading1({ children, className }: Props) {
|
||||
return (
|
||||
<h1 className={cn('text-4xl md:text-6xl font-bold', className)}>
|
||||
<h1
|
||||
className={cn('text-5xl md:text-6xl font-bold text-slate-800', className)}
|
||||
>
|
||||
{children}
|
||||
</h1>
|
||||
);
|
||||
@@ -33,8 +35,20 @@ export function Heading1({ children, className }: Props) {
|
||||
|
||||
export function Heading2({ children, className }: Props) {
|
||||
return (
|
||||
<h2 className={cn('text-2xl md:text-4xl font-bold', className)}>
|
||||
<h2
|
||||
className={cn('text-4xl md:text-5xl font-bold text-slate-800', className)}
|
||||
>
|
||||
{children}
|
||||
</h2>
|
||||
);
|
||||
}
|
||||
|
||||
export function Heading3({ children, className }: Props) {
|
||||
return (
|
||||
<h3
|
||||
className={cn('text-2xl md:text-3xl font-bold text-slate-800', className)}
|
||||
>
|
||||
{children}
|
||||
</h3>
|
||||
);
|
||||
}
|
||||
|
||||
79
apps/public/src/app/hero.tsx
Normal file
79
apps/public/src/app/hero.tsx
Normal file
@@ -0,0 +1,79 @@
|
||||
// background-image: radial-gradient(circle at 1px 1px, black 1px, transparent 0);
|
||||
// background-size: 40px 40px;
|
||||
|
||||
import { Logo } from '@/components/Logo';
|
||||
import {
|
||||
BarChart2Icon,
|
||||
CookieIcon,
|
||||
Globe2Icon,
|
||||
LayoutPanelTopIcon,
|
||||
LockIcon,
|
||||
} from 'lucide-react';
|
||||
|
||||
import { Heading1, Lead, Lead2 } from './copy';
|
||||
import { JoinWaitlist } from './join-waitlist';
|
||||
|
||||
const features = [
|
||||
{
|
||||
title: 'Great overview',
|
||||
icon: LayoutPanelTopIcon,
|
||||
},
|
||||
{
|
||||
title: 'Beautiful charts',
|
||||
icon: BarChart2Icon,
|
||||
},
|
||||
{
|
||||
title: 'Privacy focused',
|
||||
icon: LockIcon,
|
||||
},
|
||||
{
|
||||
title: 'Open-source',
|
||||
icon: Globe2Icon,
|
||||
},
|
||||
{
|
||||
title: 'No cookies',
|
||||
icon: CookieIcon,
|
||||
},
|
||||
];
|
||||
|
||||
export function Hero() {
|
||||
return (
|
||||
<div>
|
||||
<div className="absolute top-0 left-0 right-0 py-6">
|
||||
<div className="container">
|
||||
<div className="flex justify-between">
|
||||
<Logo />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div
|
||||
className="flex flex-col items-center w-full text-center text-blue-950 bg-[radial-gradient(circle_at_2px_2px,#D9DEF6_2px,transparent_0)]"
|
||||
style={{
|
||||
backgroundSize: '70px 70px',
|
||||
}}
|
||||
>
|
||||
<div className="py-20 pt-32 p-4 flex flex-col items-center max-w-3xl bg-[radial-gradient(circle,rgba(255,255,255,0.7)_0%,rgba(255,255,255,0.7)_50%,rgba(255,255,255,0)_100%)]">
|
||||
<Heading1 className="mb-4">
|
||||
A open-source
|
||||
<br />
|
||||
alternative to Mixpanel
|
||||
</Heading1>
|
||||
<p className="mb-8">
|
||||
Combine Mixpanel and Plausible and you get Openpanel. A simple
|
||||
analytics tool that respects privacy.
|
||||
</p>
|
||||
<JoinWaitlist />
|
||||
<div className="flex flex-wrap gap-10 mt-8 max-w-xl justify-center">
|
||||
{features.map(({ icon: Icon, title }) => (
|
||||
<div className="flex gap-2 items-center justify-center">
|
||||
<Icon className="text-blue-light " />
|
||||
{title}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -10,8 +10,13 @@ import {
|
||||
DialogHeader,
|
||||
DialogTitle,
|
||||
} from '@/components/ui/dialog';
|
||||
import { cn } from '@/utils/cn';
|
||||
|
||||
export function JoinWaitlist() {
|
||||
interface JoinWaitlistProps {
|
||||
className?: string;
|
||||
}
|
||||
|
||||
export function JoinWaitlist({ className }: JoinWaitlistProps) {
|
||||
const [value, setValue] = useState('');
|
||||
const [open, setOpen] = useState(false);
|
||||
|
||||
@@ -59,7 +64,10 @@ export function JoinWaitlist() {
|
||||
<div className="relative w-full mb-8">
|
||||
<input
|
||||
placeholder="Enter your email"
|
||||
className="border border-slate-100 rounded-md shadow-sm bg-white h-12 w-full px-4 outline-none focus:ring-1 ring-black"
|
||||
className={cn(
|
||||
'border border-slate-100 rounded-md shadow-sm bg-white h-12 w-full px-4 outline-none focus:ring-1 ring-black text-blue-darker',
|
||||
className
|
||||
)}
|
||||
value={value}
|
||||
onChange={(e) => setValue(e.target.value)}
|
||||
/>
|
||||
|
||||
@@ -3,6 +3,7 @@ import { cn } from '@/utils/cn';
|
||||
import '@/styles/globals.css';
|
||||
|
||||
import type { Metadata } from 'next';
|
||||
import { Bricolage_Grotesque } from 'next/font/google';
|
||||
import Script from 'next/script';
|
||||
|
||||
import { defaultMeta } from './meta';
|
||||
@@ -14,6 +15,12 @@ export const metadata: Metadata = {
|
||||
},
|
||||
};
|
||||
|
||||
const font = Bricolage_Grotesque({
|
||||
display: 'swap',
|
||||
subsets: ['latin'],
|
||||
weights: [400, 700],
|
||||
});
|
||||
|
||||
export default async function RootLayout({
|
||||
children,
|
||||
}: {
|
||||
@@ -21,7 +28,12 @@ export default async function RootLayout({
|
||||
}) {
|
||||
return (
|
||||
<html lang="en" className="light">
|
||||
<body className={cn('min-h-screen font-sans antialiased grainy')}>
|
||||
<body
|
||||
className={cn(
|
||||
'min-h-screen antialiased grainy text-slate-600',
|
||||
font.className
|
||||
)}
|
||||
>
|
||||
{children}
|
||||
</body>
|
||||
<Script
|
||||
|
||||
@@ -1,169 +1,154 @@
|
||||
import { Logo } from '@/components/Logo';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import type { LucideIcon } from 'lucide-react';
|
||||
import {
|
||||
BarChart2,
|
||||
BellIcon,
|
||||
ClockIcon,
|
||||
CloudIcon,
|
||||
CompassIcon,
|
||||
ConeIcon,
|
||||
CookieIcon,
|
||||
DollarSignIcon,
|
||||
Globe2Icon,
|
||||
KeyIcon,
|
||||
LayoutPanelTopIcon,
|
||||
LockIcon,
|
||||
UserRoundSearchIcon,
|
||||
UsersIcon,
|
||||
} from 'lucide-react';
|
||||
import Image from 'next/image';
|
||||
|
||||
import { HomeCarousel } from './carousel';
|
||||
import { Heading1, Heading2, Lead, Lead2, Paragraph } from './copy';
|
||||
import { PreviewCarousel } from './carousel';
|
||||
import { Heading2, Lead2, Paragraph } from './copy';
|
||||
import { Hero } from './hero';
|
||||
import { JoinWaitlist } from './join-waitlist';
|
||||
import { Section, Sections } from './section';
|
||||
|
||||
const features = [
|
||||
{
|
||||
title: 'Great overview',
|
||||
icon: LayoutPanelTopIcon,
|
||||
},
|
||||
{
|
||||
title: 'Beautiful charts',
|
||||
icon: BarChart2,
|
||||
},
|
||||
{
|
||||
title: 'Privacy focused',
|
||||
icon: LockIcon,
|
||||
},
|
||||
{
|
||||
title: 'Open-source',
|
||||
icon: Globe2Icon,
|
||||
},
|
||||
{
|
||||
title: 'No cookies',
|
||||
icon: CookieIcon,
|
||||
},
|
||||
{
|
||||
title: 'User journey',
|
||||
icon: UsersIcon,
|
||||
},
|
||||
];
|
||||
import { Sections } from './section';
|
||||
|
||||
export default function Page() {
|
||||
return (
|
||||
<div>
|
||||
<div className="max-w-6xl p-4 mx-auto absolute top-0 left-0 right-0 py-6">
|
||||
<div className="flex justify-between">
|
||||
<Logo />
|
||||
</div>
|
||||
<Hero />
|
||||
<div className="bg-gradient-to-b from-blue-light to-[#FFFFFF] py-16 md:py-40 text-center">
|
||||
<PreviewCarousel />
|
||||
</div>
|
||||
|
||||
<div className="flex flex-col items-center bg-gradient-to-br from-white via-white to-blue-200 w-full text-center text-blue-950">
|
||||
<div className="py-20 pt-32 p-4 flex flex-col items-center max-w-3xl ">
|
||||
<Heading1 className="mb-4 fancy-text">
|
||||
A open-source
|
||||
<div className="container">
|
||||
<div className="mb-24">
|
||||
<Heading2 className="md:text-5xl mb-2 leading-none">
|
||||
Analytics should be easy
|
||||
<br />
|
||||
alternative to Mixpanel
|
||||
</Heading1>
|
||||
<Lead className="mb-8">
|
||||
Combine Mixpanel and Plausible and you get Openpanel. A simple
|
||||
analytics tool that respects privacy.
|
||||
</Lead>
|
||||
<JoinWaitlist />
|
||||
<div className="grid grid-cols-2 md:grid-cols-3 gap-8 mt-8">
|
||||
{features.map(({ icon: Icon, title }) => (
|
||||
<div className="flex gap-2 items-center justify-center">
|
||||
<Icon />
|
||||
{title}
|
||||
and powerful
|
||||
</Heading2>
|
||||
<Lead2>
|
||||
The power of Mixpanel, the ease of Plausible and nothing from Google
|
||||
Analytics 😉
|
||||
</Lead2>
|
||||
</div>
|
||||
<Sections />
|
||||
</div>
|
||||
<div className="container mt-40">
|
||||
<div className="flex flex-col md:flex-row gap-8">
|
||||
<div className="mb-4 md:w-1/2 flex-shrink-0 relative">
|
||||
<Heading2>Another analytic tool? Really?</Heading2>
|
||||
{/* <SirenIcon
|
||||
strokeWidth={0.5}
|
||||
size={300}
|
||||
className="opacity-10 absolute -rotate-12 -left-20 -top-10"
|
||||
/> */}
|
||||
</div>
|
||||
<div className="flex flex-col gap-4 max-w-3xl">
|
||||
<h3 className="text-lg font-bold text-blue-dark">TL;DR</h3>
|
||||
<Paragraph>
|
||||
Our open-source analytic library fills a crucial gap by combining
|
||||
the strengths of Mixpanel's powerful features with Plausible's
|
||||
clear overview page. Motivated by the lack of an open-source
|
||||
alternative to Mixpanel and inspired by Plausible's simplicity, we
|
||||
aim to create an intuitive platform with predictable pricing. With
|
||||
a single-tier pricing model and limits only on monthly event
|
||||
counts, our goal is to democratize analytics, offering
|
||||
unrestricted access to all features while ensuring affordability
|
||||
and transparency for users of all project sizes.
|
||||
</Paragraph>
|
||||
|
||||
<h3 className="text-lg font-bold text-blue-dark mt-12">The why</h3>
|
||||
<Paragraph>
|
||||
Our open-source analytic library emerged from a clear need within
|
||||
the analytics community. While platforms like Mixpanel offer
|
||||
powerful and user-friendly features, they lack a comprehensive
|
||||
overview page akin to Plausible's, which succinctly summarizes
|
||||
essential metrics. Recognizing this gap, we saw an opportunity to
|
||||
combine the strengths of both platforms while addressing their
|
||||
respective shortcomings.
|
||||
</Paragraph>
|
||||
|
||||
<Paragraph>
|
||||
One significant motivation behind our endeavor was the absence of
|
||||
an open-source alternative to Mixpanel. We believe in the
|
||||
importance of accessibility and transparency in analytics, which
|
||||
led us to embark on creating a solution that anyone can freely use
|
||||
and contribute to.
|
||||
</Paragraph>
|
||||
|
||||
<Paragraph>
|
||||
Inspired by Plausible's exemplary approach to simplicity and
|
||||
clarity, we aim to build upon their foundation and further refine
|
||||
the user experience. By harnessing the best practices demonstrated
|
||||
by Plausible, we aspire to create an intuitive and streamlined
|
||||
analytics platform that empowers users to derive actionable
|
||||
insights effortlessly.
|
||||
</Paragraph>
|
||||
|
||||
<Paragraph>
|
||||
Our own experiences with traditional analytics platforms like
|
||||
Mixpanel underscored another critical aspect driving our project:
|
||||
the need for predictable pricing. As project owners ourselves, we
|
||||
encountered the frustration of escalating costs as our user base
|
||||
grew. Therefore, we are committed to offering a single-tier
|
||||
pricing model that provides unlimited access to all features
|
||||
without the fear of unexpected expenses.
|
||||
</Paragraph>
|
||||
|
||||
<Paragraph>
|
||||
In line with our commitment to fairness and accessibility, our
|
||||
pricing model will only impose limits on the number of events
|
||||
users can send each month. This approach, akin to Plausible's,
|
||||
ensures that users have the freedom to explore and utilize our
|
||||
platform to its fullest potential without arbitrary restrictions
|
||||
on reports or user counts. Ultimately, our goal is to democratize
|
||||
analytics by offering a reliable, transparent, and cost-effective
|
||||
solution for projects of all sizes.
|
||||
</Paragraph>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<footer className="bg-blue-darker text-white relative mt-40 relative">
|
||||
<div className="inset-0 absolute h-full w-full bg-[radial-gradient(circle,rgba(255,255,255,0.2)_0%,rgba(255,255,255,0)_100%)]"></div>
|
||||
<div className="relative container flex flex-col items-center text-center">
|
||||
<div className="my-24">
|
||||
<Heading2 className="text-white mb-2">Get early access</Heading2>
|
||||
<Lead2>
|
||||
Ready to set your analytics free? Get on our waitlist.
|
||||
</Lead2>
|
||||
|
||||
<div className="mt-8">
|
||||
<JoinWaitlist className="text-white bg-white/20 border-white/30 focus:ring-white" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="overflow-hidden rounded-xl">
|
||||
<div className="p-3 bg-white/20">
|
||||
<Image
|
||||
src="/demo/overview-min.png"
|
||||
width={1080}
|
||||
height={608}
|
||||
alt="Openpanel overview page"
|
||||
className="w-full rounded-lg"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="absolute bottom-0 left-0 right-0">
|
||||
<div className="h-px w-full bg-[radial-gradient(circle,rgba(255,255,255,0.7)_0%,rgba(255,255,255,0.7)_50%,rgba(255,255,255,0)_100%)]"></div>
|
||||
<div className="p-4 bg-blue-darker">
|
||||
<div className="container">
|
||||
<div className="flex justify-between items-center text-sm">
|
||||
<Logo />
|
||||
<a
|
||||
className="hover:underline"
|
||||
href="https://twitter.com/CarlLindesvard"
|
||||
target="_blank"
|
||||
rel="nofollow"
|
||||
>
|
||||
Follow on X
|
||||
</a>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<Sections />
|
||||
<div className="bg-blue-800 p-4 py-8 md:py-16 text-center">
|
||||
<Heading2 className="text-slate-100 mb-4">
|
||||
Get a feel how it looks
|
||||
</Heading2>
|
||||
<Lead className="text-slate-200 mb-16">
|
||||
We've crafted a clean and intuitive interface because analytics should
|
||||
<br />
|
||||
be straightforward, unlike the complexity often associated with Google
|
||||
Analytics. 😅
|
||||
</Lead>
|
||||
<HomeCarousel />
|
||||
</div>
|
||||
<div className="p-4 py-8 md:py-16 text-center flex flex-col items-center">
|
||||
<Heading2 className="mb-4">Another analytic tool?</Heading2>
|
||||
<div className="flex flex-col gap-4 max-w-3xl">
|
||||
<Paragraph>
|
||||
<strong>TL;DR</strong> Our open-source analytic library fills a
|
||||
crucial gap by combining the strengths of Mixpanel's powerful
|
||||
features with Plausible's clear overview page. Motivated by the lack
|
||||
of an open-source alternative to Mixpanel and inspired by
|
||||
Plausible's simplicity, we aim to create an intuitive platform with
|
||||
predictable pricing. With a single-tier pricing model and limits
|
||||
only on monthly event counts, our goal is to democratize analytics,
|
||||
offering unrestricted access to all features while ensuring
|
||||
affordability and transparency for users of all project sizes.
|
||||
</Paragraph>
|
||||
|
||||
<div className="flex gap-2 w-full justify-center my-8">
|
||||
<div className="rounded-full h-2 w-10 bg-blue-200"></div>
|
||||
<div className="rounded-full h-2 w-10 bg-blue-400"></div>
|
||||
<div className="rounded-full h-2 w-10 bg-blue-600"></div>
|
||||
<div className="rounded-full h-2 w-10 bg-blue-800"></div>
|
||||
</div>
|
||||
|
||||
<Paragraph>
|
||||
Our open-source analytic library emerged from a clear need within
|
||||
the analytics community. While platforms like Mixpanel offer
|
||||
powerful and user-friendly features, they lack a comprehensive
|
||||
overview page akin to Plausible's, which succinctly summarizes
|
||||
essential metrics. Recognizing this gap, we saw an opportunity to
|
||||
combine the strengths of both platforms while addressing their
|
||||
respective shortcomings.
|
||||
</Paragraph>
|
||||
|
||||
<Paragraph>
|
||||
One significant motivation behind our endeavor was the absence of an
|
||||
open-source alternative to Mixpanel. We believe in the importance of
|
||||
accessibility and transparency in analytics, which led us to embark
|
||||
on creating a solution that anyone can freely use and contribute to.
|
||||
</Paragraph>
|
||||
|
||||
<Paragraph>
|
||||
Inspired by Plausible's exemplary approach to simplicity and
|
||||
clarity, we aim to build upon their foundation and further refine
|
||||
the user experience. By harnessing the best practices demonstrated
|
||||
by Plausible, we aspire to create an intuitive and streamlined
|
||||
analytics platform that empowers users to derive actionable insights
|
||||
effortlessly.
|
||||
</Paragraph>
|
||||
|
||||
<Paragraph>
|
||||
Our own experiences with traditional analytics platforms like
|
||||
Mixpanel underscored another critical aspect driving our project:
|
||||
the need for predictable pricing. As project owners ourselves, we
|
||||
encountered the frustration of escalating costs as our user base
|
||||
grew. Therefore, we are committed to offering a single-tier pricing
|
||||
model that provides unlimited access to all features without the
|
||||
fear of unexpected expenses.
|
||||
</Paragraph>
|
||||
|
||||
<Paragraph>
|
||||
In line with our commitment to fairness and accessibility, our
|
||||
pricing model will only impose limits on the number of events users
|
||||
can send each month. This approach, akin to Plausible's, ensures
|
||||
that users have the freedom to explore and utilize our platform to
|
||||
its fullest potential without arbitrary restrictions on reports or
|
||||
user counts. Ultimately, our goal is to democratize analytics by
|
||||
offering a reliable, transparent, and cost-effective solution for
|
||||
projects of all sizes.
|
||||
</Paragraph>
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -3,14 +3,41 @@
|
||||
import { cn } from '@/utils/cn';
|
||||
import type { LucideIcon, LucideProps } from 'lucide-react';
|
||||
import {
|
||||
ArrowUpFromDotIcon,
|
||||
BarChart2Icon,
|
||||
BellIcon,
|
||||
BookmarkIcon,
|
||||
CheckCircle,
|
||||
ClockIcon,
|
||||
CloudIcon,
|
||||
CloudLightning,
|
||||
CloudLightningIcon,
|
||||
CompassIcon,
|
||||
ConeIcon,
|
||||
DatabaseIcon,
|
||||
DollarSignIcon,
|
||||
DownloadIcon,
|
||||
FileIcon,
|
||||
FilterIcon,
|
||||
FolderIcon,
|
||||
FolderOpenIcon,
|
||||
HandCoinsIcon,
|
||||
HandshakeIcon,
|
||||
KeyIcon,
|
||||
PieChartIcon,
|
||||
PointerIcon,
|
||||
RouteIcon,
|
||||
ServerIcon,
|
||||
ShieldPlusIcon,
|
||||
ShoppingCartIcon,
|
||||
SquareUserRound,
|
||||
StarIcon,
|
||||
ThumbsUp,
|
||||
ThumbsUpIcon,
|
||||
TrendingUpIcon,
|
||||
UserRoundSearchIcon,
|
||||
UsersIcon,
|
||||
WebhookIcon,
|
||||
} from 'lucide-react';
|
||||
|
||||
import {
|
||||
@@ -25,6 +52,7 @@ import {
|
||||
Blob9,
|
||||
} from './blob';
|
||||
import { Heading2, Lead2 } from './copy';
|
||||
import { Widget } from './widget';
|
||||
|
||||
interface SectionItem {
|
||||
title: string;
|
||||
@@ -32,7 +60,8 @@ interface SectionItem {
|
||||
icon: LucideIcon;
|
||||
color: string;
|
||||
soon?: string;
|
||||
blob: React.ComponentType<LucideProps>;
|
||||
icons: LucideIcon[];
|
||||
className: string;
|
||||
}
|
||||
|
||||
const sections: SectionItem[] = [
|
||||
@@ -42,7 +71,8 @@ const sections: SectionItem[] = [
|
||||
'Take control of your data privacy and ownership with our platform, ensuring full transparency and security.',
|
||||
icon: KeyIcon,
|
||||
color: '#2563EB',
|
||||
blob: Blob1,
|
||||
icons: [FolderIcon, DatabaseIcon, ShieldPlusIcon, KeyIcon],
|
||||
className: 'bg-blue-light',
|
||||
},
|
||||
{
|
||||
title: 'Cloud or Self-Hosting',
|
||||
@@ -50,7 +80,8 @@ const sections: SectionItem[] = [
|
||||
'Choose between the flexibility of cloud-based hosting or the autonomy of self-hosting to tailor your analytics infrastructure to your needs.',
|
||||
icon: CloudIcon,
|
||||
color: '#ff7557',
|
||||
blob: Blob2,
|
||||
icons: [CloudIcon, CheckCircle, ServerIcon, DownloadIcon],
|
||||
className: '', // 'bg-[#ff7557]',
|
||||
},
|
||||
{
|
||||
title: 'Real-Time Events',
|
||||
@@ -58,15 +89,17 @@ const sections: SectionItem[] = [
|
||||
'Stay up-to-date with real-time event tracking, enabling instant insights into user actions as they happen.',
|
||||
icon: ClockIcon,
|
||||
color: '#7fe1d8',
|
||||
blob: Blob3,
|
||||
icons: [CloudLightningIcon, ShoppingCartIcon, ArrowUpFromDotIcon],
|
||||
className: '', // bg-[#7fe1d8]
|
||||
},
|
||||
{
|
||||
title: 'Deep Dive into User Behavior',
|
||||
title: 'Deep Dive into User Behaviors',
|
||||
description:
|
||||
"Gain profound insights into user behavior with comprehensive analytics tools, allowing you to understand your audience's actions and preferences.",
|
||||
icon: UserRoundSearchIcon,
|
||||
color: '#f8bc3c',
|
||||
blob: Blob4,
|
||||
icons: [UsersIcon, RouteIcon, BookmarkIcon],
|
||||
className: 'bg-blue-dark', //'bg-[#f8bc3c]',
|
||||
},
|
||||
{
|
||||
title: 'Powerful Report Explorer',
|
||||
@@ -74,7 +107,8 @@ const sections: SectionItem[] = [
|
||||
'Explore and analyze your data effortlessly with our powerful report explorer, simplifying the process of deriving meaningful insights.',
|
||||
icon: CompassIcon,
|
||||
color: '#b3596e',
|
||||
blob: Blob5,
|
||||
icons: [ThumbsUpIcon, TrendingUpIcon, PieChartIcon, BarChart2Icon],
|
||||
className: 'bg-[#ff7557]',
|
||||
},
|
||||
{
|
||||
soon: 'Coming soon',
|
||||
@@ -83,7 +117,8 @@ const sections: SectionItem[] = [
|
||||
'Track user conversion funnels seamlessly, providing valuable insights into user journey optimization.',
|
||||
icon: ConeIcon,
|
||||
color: '#72bef4',
|
||||
blob: Blob6,
|
||||
icons: [ConeIcon, FilterIcon],
|
||||
className: '', //'bg-[#72bef4]',
|
||||
},
|
||||
{
|
||||
soon: 'Coming with our native app',
|
||||
@@ -92,7 +127,8 @@ const sections: SectionItem[] = [
|
||||
'Stay informed about conversions, events, and peaks with our upcoming push notification tool, empowering you to monitor and respond to critical activities in real-time.',
|
||||
icon: BellIcon,
|
||||
color: '#ffb27a',
|
||||
blob: Blob7,
|
||||
icons: [WebhookIcon, BellIcon],
|
||||
className: '', //'bg-[#ffb27a]',
|
||||
},
|
||||
{
|
||||
title: 'Cost-Effective Alternative to Mixpanel',
|
||||
@@ -100,7 +136,8 @@ const sections: SectionItem[] = [
|
||||
'Enjoy the same powerful analytics capabilities as Mixpanel at a fraction of the cost, ensuring affordability without compromising on quality.',
|
||||
icon: DollarSignIcon,
|
||||
color: '#0f7ea0',
|
||||
blob: Blob8,
|
||||
icons: [DollarSignIcon, HandCoinsIcon, HandshakeIcon, StarIcon],
|
||||
className: 'bg-[#3ba974]',
|
||||
},
|
||||
{
|
||||
soon: 'Something Plausible lacks',
|
||||
@@ -113,61 +150,59 @@ const sections: SectionItem[] = [
|
||||
);
|
||||
}) as unknown as LucideIcon,
|
||||
color: '#3ba974',
|
||||
blob: Blob9,
|
||||
icons: [FolderIcon, DatabaseIcon, ShieldPlusIcon, KeyIcon],
|
||||
className: 'bg-[#f8bc3c]',
|
||||
},
|
||||
];
|
||||
|
||||
interface SectionProps extends SectionItem {
|
||||
reverse?: boolean;
|
||||
}
|
||||
export function Section({
|
||||
title,
|
||||
description,
|
||||
icon: Icon,
|
||||
blob: Blob,
|
||||
color,
|
||||
soon,
|
||||
reverse,
|
||||
}: SectionProps) {
|
||||
return (
|
||||
<div key={title} className={'border-b border-border'}>
|
||||
<div className="w-full max-w-6xl mx-auto px-4">
|
||||
<div
|
||||
className={cn(
|
||||
'flex py-16 flex-col justify-center',
|
||||
reverse ? 'md:flex-row' : 'md:flex-row-reverse'
|
||||
)}
|
||||
>
|
||||
<div className="md:w-1/2 flex-shrink-0 justify-center items-center flex max-md:mb-8 overflow-hidden rounded-lg">
|
||||
<div className="bg-slate-50 rounded-3xl">
|
||||
<Blob
|
||||
style={{ fill: color }}
|
||||
className="w-[600px] opacity-20 transition-transform animate-[spin_60s_ease-in-out_infinite] -m-[100px]"
|
||||
/>
|
||||
</div>
|
||||
<Icon className="w-40 h-40 absolute" strokeWidth={2} />
|
||||
</div>
|
||||
<div className="justify-center flex-col flex">
|
||||
{!!soon && (
|
||||
<div className="rounded-full border border-border p-2 px-4 leading-none mb-4 self-start">
|
||||
{soon}
|
||||
</div>
|
||||
)}
|
||||
<Heading2 className="mb-4">{title}</Heading2>
|
||||
<Lead2>{description}</Lead2>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
// To lazy to think now...
|
||||
function checkIndex(index: number) {
|
||||
switch (index) {
|
||||
case 0:
|
||||
case 3:
|
||||
case 4:
|
||||
case 7:
|
||||
case 8:
|
||||
case 10:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
export function Sections() {
|
||||
return (
|
||||
<>
|
||||
{sections.map((section, index) => (
|
||||
<Section key={index} {...section} reverse={index % 2 === 1} />
|
||||
))}
|
||||
<div className="grid grid-cols-1 md:grid-cols-3 gap-8 gap-y-16">
|
||||
{sections.map((section, i) => {
|
||||
const even = checkIndex(i);
|
||||
|
||||
const offsets = even
|
||||
? [
|
||||
'-top-10 -left-10 rotate-12',
|
||||
'top-10 -rotate-12',
|
||||
'-right-5',
|
||||
'-right-10 -top-20',
|
||||
]
|
||||
: ['-top-10 -left-20 rotate-12', 'top-10 -rotate-12', '-right-5'];
|
||||
|
||||
const className = even
|
||||
? cn('text-white [&_h3]:text-white col-span-2', section.className)
|
||||
: cn('border border-border', section.className);
|
||||
|
||||
return (
|
||||
<Widget
|
||||
key={section.title}
|
||||
title={section.title}
|
||||
className={className}
|
||||
icons={section.icons}
|
||||
offsets={offsets}
|
||||
>
|
||||
<p>{section.description}</p>
|
||||
</Widget>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
43
apps/public/src/app/widget.tsx
Normal file
43
apps/public/src/app/widget.tsx
Normal file
@@ -0,0 +1,43 @@
|
||||
import type { ReactNode } from 'react';
|
||||
import { cn } from '@/utils/cn';
|
||||
import type { LucideIcon } from 'lucide-react';
|
||||
|
||||
import { Heading3 } from './copy';
|
||||
|
||||
interface WidgetProps {
|
||||
title: string;
|
||||
children: ReactNode;
|
||||
className?: string;
|
||||
icons: LucideIcon[];
|
||||
offsets: string[];
|
||||
}
|
||||
|
||||
export function Widget({
|
||||
title,
|
||||
children,
|
||||
className,
|
||||
icons,
|
||||
offsets,
|
||||
}: WidgetProps) {
|
||||
return (
|
||||
<div
|
||||
className={cn(
|
||||
'p-10 rounded-xl relative overflow-hidden flex flex-col hover:scale-105 transition-all duration-300 ease-in-out bg-white hover:shadow min-h-[300px] max-md:col-span-3',
|
||||
className
|
||||
)}
|
||||
>
|
||||
<Heading3 className="mb-4">{title}</Heading3>
|
||||
{children}
|
||||
<div className="flex justify-between mt-auto">
|
||||
{icons.map((Icon, i) => (
|
||||
<Icon
|
||||
key={i}
|
||||
size={120}
|
||||
className={cn('flex-shrink-0 opacity-10 relative', offsets?.[i])}
|
||||
strokeWidth={1.5}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user