public: hero improvements

This commit is contained in:
Carl-Gerhard Lindesvärd
2024-03-14 07:00:11 +01:00
parent f276deeeeb
commit d3a57178b6
15 changed files with 179 additions and 57 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.8 KiB

View File

@@ -0,0 +1,20 @@
import { Navbar } from '../navbar';
interface Props {
children: React.ReactNode;
}
export default function Layout({ children }: Props) {
return (
<>
<Navbar darkText />
<div
className="opacity-50 w-full h-screen text-blue-950 bg-[radial-gradient(circle_at_2px_2px,#D9DEF6_2px,transparent_0)] absolute top-0 left-0 right-0 z-0"
style={{
backgroundSize: '70px 70px',
}}
/>
<div className="relative">{children}</div>
</>
);
}

View File

@@ -1,4 +1,4 @@
import { Heading1 } from '../copy'; import { Heading1 } from '../../copy';
export const dynamic = 'force-dynamic'; export const dynamic = 'force-dynamic';
export const revalidate = 3600; export const revalidate = 3600;

View File

@@ -1,4 +1,4 @@
import { Heading1 } from '../copy'; import { Heading1 } from '../../copy';
export const dynamic = 'force-dynamic'; export const dynamic = 'force-dynamic';
export const revalidate = 3600; export const revalidate = 3600;

View File

@@ -1,8 +1,12 @@
import { Tooltip, TooltipContent } from '@/components/ui/tooltip';
import { TooltipTrigger } from '@radix-ui/react-tooltip';
import Image from 'next/image'; import Image from 'next/image';
import { PreviewCarousel } from './carousel'; import { PreviewCarousel } from './carousel';
import { Heading1, Lead2 } from './copy'; import { Heading1, Lead2 } from './copy';
import { JoinWaitlistHero } from './join-waitlist-hero'; import { JoinWaitlistHero } from './join-waitlist-hero';
import { SocialProofServer } from './social-proof';
import { SocialProof } from './social-proof/social-proof';
const avatars = [ const avatars = [
'https://api.dicebear.com/7.x/adventurer/svg?seed=Chester&backgroundColor=b6e3f4', 'https://api.dicebear.com/7.x/adventurer/svg?seed=Chester&backgroundColor=b6e3f4',
@@ -10,7 +14,7 @@ const avatars = [
'https://api.dicebear.com/7.x/adventurer/svg?seed=Boo&backgroundColor=ffdfbf', 'https://api.dicebear.com/7.x/adventurer/svg?seed=Boo&backgroundColor=ffdfbf',
]; ];
export function Hero({ waitlistCount }: { waitlistCount: number }) { export function Hero() {
return ( return (
<div className="flex py-32 flex-col items-center w-full text-center bg-[#1F54FF] relative overflow-hidden"> <div className="flex py-32 flex-col items-center w-full text-center bg-[#1F54FF] relative overflow-hidden">
{/* <div className="inset-0 absolute h-full w-full bg-[radial-gradient(circle,rgba(255,255,255,0.1)_0%,rgba(255,255,255,0)_100%)]"></div> */} {/* <div className="inset-0 absolute h-full w-full bg-[radial-gradient(circle,rgba(255,255,255,0.1)_0%,rgba(255,255,255,0)_100%)]"></div> */}
@@ -37,16 +41,12 @@ export function Hero({ waitlistCount }: { waitlistCount: number }) {
alternative to Mixpanel alternative to Mixpanel
</Heading1> </Heading1>
<Lead2 className="text-white/70 font-light"> <Lead2 className="text-white/70 font-light">
Mixpanel + Plausible = <span className="text-white">Openpanel!</span>{' '} The power of Mixpanel, the ease of Plausible <br />
<br />A simple analytics tool that your wallet can afford. and nothing from Google Analytics 😉
</Lead2> </Lead2>
<div className="my-12 w-full flex flex-col items-center"> <div className="my-12 w-full flex flex-col items-center">
<JoinWaitlistHero /> <JoinWaitlistHero />
<div className="mt-6 flex justify-center items-center"> <SocialProofServer className="mt-6" />
<p className="text-white">
{waitlistCount} people have already signed up! 🚀
</p>
</div>
</div> </div>
</div> </div>

View File

@@ -25,14 +25,14 @@ export function JoinWaitlistHero({ className }: JoinWaitlistProps) {
useEffect(() => { useEffect(() => {
if (open) { if (open) {
// @ts-ignore // @ts-ignore
window.op('event', 'waitlist_open'); window.op?.('event', 'waitlist_open');
} }
}, [open]); }, [open]);
useEffect(() => { useEffect(() => {
if (success) { if (success) {
// @ts-ignore // @ts-ignore
window.op('event', 'waitlist_success', { window.op?.('event', 'waitlist_success', {
email: value, email: value,
}); });
} }
@@ -113,15 +113,23 @@ export function JoinWaitlistHero({ className }: JoinWaitlistProps) {
> >
Join waitlist now Join waitlist now
</Button> </Button>
<Link
href="https://dashboard.openpanel.dev/share/overview/ZQsEhG" <div className="relative">
target="_blank" <Link
rel="nofollow" href="https://dashboard.openpanel.dev/share/overview/ZQsEhG"
> target="_blank"
<Button size="lg" variant="outline" className="text-lg h-12"> rel="nofollow"
Demo >
</Button> <Button size="lg" variant="outline" className="text-lg h-12">
</Link> Demo
</Button>
</Link>
<img
src="/clickable-demo.png"
className="w-44 shrink-0 absolute left-full -top-8 translate-x-10 max-w-none"
alt="Clickable demo button"
/>
</div>
</div> </div>
</> </>
); );

View File

@@ -35,39 +35,10 @@ export default function RootLayout({
font.className font.className
)} )}
> >
<div {children}
className="w-full h-screen text-blue-950 bg-[radial-gradient(circle_at_2px_2px,#D9DEF6_2px,transparent_0)] absolute top-0 left-0 right-0 z-0"
style={{
backgroundSize: '70px 70px',
}}
/>
<div className="relative">{children}</div>
<Footer /> <Footer />
</body> </body>
<Script {/* 301c6dc1-424c-4bc3-9886-a8beab09b615 */}
src="https://openpanel.dev/op.js"
async
defer
strategy="afterInteractive"
/>
<Script
id="openpanel"
dangerouslySetInnerHTML={{
__html: `
window.op =
window.op ||
function (...args) {
(window.op.q = window.op.q || []).push(args);
};
window.op('ctor', {
clientId: '301c6dc1-424c-4bc3-9886-a8beab09b615',
trackScreenViews: true,
trackOutgoingLinks: true,
trackAttributes: true,
});
`,
}}
/>
</html> </html>
); );
} }

View File

@@ -0,0 +1,34 @@
'use client';
import { Logo } from '@/components/Logo';
import { cn } from '@/utils/cn';
import Link from 'next/link';
import { usePathname } from 'next/navigation';
interface Props {
darkText?: boolean;
className?: string;
}
export function Navbar({ darkText = false, className }: Props) {
const pathname = usePathname();
const textColor = darkText ? 'text-blue-dark' : 'text-white';
return (
<div
className={cn('absolute top-0 left-0 right-0 z-10', textColor, className)}
>
<div className="container flex justify-between items-center py-4">
<Logo />
<nav className="flex gap-4">
{pathname !== '/' && <Link href="/">Home</Link>}
<a href="https://docs.openpanel.dev" target="_blank">
Docs
</a>
<a href="https://dashboard.openpanel.dev" target="_blank">
Sign in
</a>
</nav>
</div>
</div>
);
}

View File

@@ -1,18 +1,18 @@
import { db } from '@openpanel/db'; import { db } from '@openpanel/db';
import { PreviewCarousel } from './carousel';
import { Heading2, Lead2, Paragraph } from './copy'; import { Heading2, Lead2, Paragraph } from './copy';
import { Hero } from './hero'; import { Hero } from './hero';
import { Navbar } from './navbar';
import { Sections } from './section'; import { Sections } from './section';
export const dynamic = 'force-dynamic'; export const dynamic = 'force-dynamic';
export const revalidate = 3600; export const revalidate = 3600;
export default async function Page() { export default function Page() {
const waitlistCount = await db.waitlist.count();
return ( return (
<div> <div>
<Hero waitlistCount={waitlistCount} /> <Navbar darkText={false} className="[&_img]:hidden" />
<Hero />
<div className="container"> <div className="container">
<div className="my-24"> <div className="my-24">
<Heading2 className="md:text-5xl mb-2 leading-none"> <Heading2 className="md:text-5xl mb-2 leading-none">

View File

@@ -0,0 +1,17 @@
import { TooltipProvider } from '@/components/ui/tooltip';
import { db } from '@openpanel/db';
import { SocialProof } from './social-proof';
interface Props {
className?: string;
}
export async function SocialProofServer(props: Props) {
const waitlistCount = await db.waitlist.count();
return (
<TooltipProvider>
<SocialProof count={waitlistCount} {...props} />;
</TooltipProvider>
);
}

View File

@@ -0,0 +1,72 @@
'use client';
import { useEffect, useState } from 'react';
import { Button } from '@/components/ui/button';
import {
Dialog,
DialogContent,
DialogDescription,
DialogFooter,
DialogHeader,
DialogTitle,
} from '@/components/ui/dialog';
import {
Tooltip,
TooltipContent,
TooltipTrigger,
} from '@/components/ui/tooltip';
import { cn } from '@/utils/cn';
import Image from 'next/image';
interface JoinWaitlistProps {
className?: string;
count: number;
}
export function SocialProof({ className, count }: JoinWaitlistProps) {
return (
<div className={cn('flex gap-2 justify-center items-center', className)}>
<div className="flex">
<Tooltip>
<TooltipTrigger>
<Image
className="rounded-full"
src="/clickhouse.png"
width={24}
height={24}
alt="Clickhouse"
/>
</TooltipTrigger>
<TooltipContent>Clickhouse is here</TooltipContent>
</Tooltip>
<Tooltip>
<TooltipTrigger className="-mx-3">
<Image
className="rounded-full"
src="/getdreams.png"
width={24}
height={24}
alt="GetDreams"
/>
</TooltipTrigger>
<TooltipContent>GetDreams is here</TooltipContent>
</Tooltip>
<Tooltip>
<TooltipTrigger>
<Image
className="rounded-full"
src="/kiddokitchen.png"
width={24}
height={24}
alt="KiddoKitchen"
/>
</TooltipTrigger>
<TooltipContent>KiddoKitchen is here</TooltipContent>
</Tooltip>
</div>
<p className="text-white">
{count} early birds have already signed up! 🚀
</p>
</div>
);
}

View File

@@ -37,7 +37,7 @@ const DialogContent = React.forwardRef<
<DialogPrimitive.Content <DialogPrimitive.Content
ref={ref} ref={ref}
className={cn( className={cn(
'fixed left-[50%] top-[50%] z-50 grid w-full max-w-lg translate-x-[-50%] translate-y-[-50%] gap-4 border bg-background p-6 shadow-lg duration-200 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[state=closed]:slide-out-to-left-1/2 data-[state=closed]:slide-out-to-top-[48%] data-[state=open]:slide-in-from-left-1/2 data-[state=open]:slide-in-from-top-[48%] sm:rounded-lg md:w-full', 'fixed left-[50%] top-[50%] z-50 grid w-full max-w-lg translate-x-[-50%] translate-y-[-100%] md:translate-y-[-50%] gap-4 border bg-background p-6 shadow-lg duration-200 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[state=closed]:slide-out-to-left-1/2 data-[state=closed]:slide-out-to-top-[48%] data-[state=open]:slide-in-from-left-1/2 data-[state=open]:slide-in-from-top-[48%] sm:rounded-lg md:w-full',
className className
)} )}
{...props} {...props}