Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[SWA-198][INFRA] - Track click events on Landing page "Launch App" buttons #130

Merged
1 change: 1 addition & 0 deletions packages/landing/.env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
NEXT_PUBLIC_FATHOM_SITE_ID=
32 changes: 32 additions & 0 deletions packages/landing/analytics/constants.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
const LAUNCH_APP = "click/launch-app";
const LAUNCH_APP_NAVBAR = `${LAUNCH_APP}-navbar`;
const FAQ = "click/faq";
const FAQ_WHAT_IS = `${FAQ}-what-is`;
const FAQ_HOW = `${FAQ}-how`;

export const EVENTS = {
NAVBAR: {
DESKTOP: {
LAUNCH_APP_CLICK: `${LAUNCH_APP_NAVBAR}-desktop`,
},
MOBILE: {
LAUNCH_APP_CLICK: `${LAUNCH_APP_NAVBAR}-mobile`,
},
},
SECTIONS: {
FAQ: {
HOW_CANCEL_STACK: `${FAQ_HOW}-cancel-stack`,
HOW_STACKLY_WORKS: `${FAQ_HOW}-stackly-works`,
WHAT_IS_STACKLY_CLICK: `${FAQ_WHAT_IS}-stackly`,
WHAT_IS_STACK_CLICK: `${FAQ_WHAT_IS}-stack`,
WHAT_IS_DCA: `${FAQ_WHAT_IS}-dca`,
WHY_TO_DCA: `${FAQ}-why-to-dca`,
},
HERO_BANNER: {
STACK_NOW_CLICK: `${LAUNCH_APP}-hero-banner`,
},
TRY_STACKLY_BANNER: {
TRY_STACKLY_NOW_CLICK: `${LAUNCH_APP}-try-stackly-banner`,
},
},
};
2 changes: 2 additions & 0 deletions packages/landing/analytics/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export * from "./constants";
export * from "./types";
25 changes: 25 additions & 0 deletions packages/landing/analytics/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
type PageViewOptions = {
referrer?: string;
url?: string;
};

export type EventName = string;

export type EventOptions = {
_site_id?: string;
_value?: number;
};

export interface Fathom {
blockTrackingForMe: () => void;
enableTrackingForMe: () => void;
setSite: (siteId: string) => void;
trackEvent(eventId: string, opts?: EventOptions): void;
trackPageview: (opts?: PageViewOptions) => void;
}

declare global {
interface Window {
fathom: Fathom;
}
}
21 changes: 18 additions & 3 deletions packages/landing/app/layout.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
import { Metadata } from "next";
import localFont from "next/font/local";
import { Metadata } from "next";
import Script from "next/script";

import { Navbar } from "@/components";
import { Providers } from "@/providers";
import { STACKLY_LANDING_URL } from "@/constants";

import "@/styles/global.css";

const stabilGrotesk = localFont({
Expand Down Expand Up @@ -34,6 +37,8 @@ export const metadata: Metadata = {
"Stackly is a simple, non-custodial tool that uses the CoW protocol to place recurring swaps based on DCA",
};

const siteId = process.env.NEXT_PUBLIC_FATHOM_SITE_ID;

export default function RootLayout({
children,
}: {
Expand All @@ -42,8 +47,18 @@ export default function RootLayout({
return (
<html lang="en" className={stabilGrotesk.variable}>
<body className="font-sans bg-surface-25 text-em-high">
<Navbar />
{children}
<Script
src="https://cdn.usefathom.com/script.js"
data-auto
data-site={siteId}
data-spa="hash"
defer
/>

<Providers>
<Navbar />
{children}
</Providers>
</body>
</html>
);
Expand Down
137 changes: 7 additions & 130 deletions packages/landing/app/page.tsx
Original file line number Diff line number Diff line change
@@ -1,97 +1,21 @@
import { PropsWithChildren } from "react";

import Link from "next/link";
import Image from "next/image";

import { BodyText, ButtonLink, HeadingText, Icon } from "@/ui";
import {
BodyText,
ButtonLink,
DisplayText,
HeadingText,
Icon,
TitleText,
} from "@/ui";
import {
STACKLY_APP_URL,
STACKLY_DISCORD_URL,
STACKLY_TWITTER_URL,
SWAPR_URL,
} from "@/constants";
import { StacklyLogoIcon } from "@/public/assets";
import { PropsWithChildren } from "react";
import { QAndAAccordion, TryStacklyBanner } from "@/components";
import { FAQ, HeroBanner, TryStacklyBanner } from "@/components";

export default function Home() {
return (
<main>
<section className="px-6 pt-16 border-b border-gray-100 md:pt-20">
<div className="space-y-4 text-center md:space-y-6 ">
<DisplayText>DCA simplified</DisplayText>
<HeadingText className="!font-medium text-em-med max-w-2xl mx-auto">
Say goodbye to market timing and hello to effortless recurrent
swaps.
</HeadingText>
</div>
<ButtonLink
target="_blank"
href={STACKLY_APP_URL}
size="lg"
width="fit"
className="!py-4 mx-auto text-lg !px-16 md:!px-28 mt-8"
>
Stack now
</ButtonLink>
<div className="relative max-w-4xl mx-auto mt-12 mb-24 md:my-20">
<Link
passHref
href={STACKLY_APP_URL}
className="relative block mx-auto w-fit"
>
<div className="invisible sm:visible absolute w-[3px] h-[26px] bg-em-med bottom-[60px] left-[17px] animate-cursor-blink"></div>
<Image
className="mx-auto border shadow-xl hover:shadow-2xl rounded-2xl border-surface-50"
alt="amount widget"
src="/assets/images/landing-widget.png"
height={200}
width={512}
/>
</Link>
<div className="absolute w-full -top-36 -z-10 h-[460px] md:bg-radial-gradient"></div>
</div>
<Link
href="https://ipfs.io/ipfs/QmUmmFkKvktZ14iA3237WuDrzNuhi4BMb4MoYMJHeFFbey"
target="_blank"
>
<div className="mx-auto flex items-center px-5 py-3 bg-primary-50 rounded-[20px] w-fit space-x-3 my-20 shadow-sm hover:shadow-md hover:bg-primary-75">
<Icon name="check" className="text-primary-600" />
<TitleText>Stackly has undergone an audit by Omega.</TitleText>
<Image
alt="omega team logo"
src="/assets/images/omega-team-logo.svg"
height={30}
width={30}
/>
</div>
</Link>

{false && (
<div className="mx-auto flex items-center px-5 py-2 bg-black/5 rounded-[20px] w-fit space-x-6 mt-20">
<div className="flex items-center space-x-2">
<BodyText size={3} weight="medium" className="text-em-med">
Total Stacks created:
</BodyText>
<BodyText size={3} weight="bold">
732
</BodyText>
</div>
<div className="flex items-center space-x-2">
<BodyText size={3} weight="medium" className="text-em-med">
Total transactions:
</BodyText>
<BodyText size={3} weight="bold">
1232
</BodyText>
</div>
</div>
)}
</section>
<HeroBanner />
<section
className="py-20 bg-white border-b border-gray-100 md:py-32"
id="how-it-works"
Expand Down Expand Up @@ -136,54 +60,7 @@ export default function Home() {
</div>
</div>
</section>
<section className="px-6 py-12 md:py-32" id="faqs">
<div className="max-w-6xl mx-auto">
<div className="flex flex-col md:flex-row md:justify-between">
<HeadingText size={4} className="pb-10 md:pb-0">
Frequently asked questions
</HeadingText>
<div className="w-full max-w-lg space-y-4">
<QAndAAccordion question="What is Stackly?" startOpen>
Stackly is a simple non-custodial DCA app that makes it easy to
do recurring swaps of any token.
</QAndAAccordion>
<QAndAAccordion question="What is a stack?">
<p>
We call it stack the creation of the recurrent order with the
total amount that will be used to swap the choosen tokens on
the choosen frequency (hourly, daily, etc).
</p>
<p>
Example: A stack of WETH using 500WXDAI that will do recurrent
swaps every day till the end of the week.
</p>
</QAndAAccordion>
<QAndAAccordion question="How does Stackly work?">
When you stack a token, stackly creates a contract for you with
the funds and uses CoW protocol to place recurring orders
(stacks) at the frequency you choose.
</QAndAAccordion>
<QAndAAccordion question="What is DCA?">
DCA stands for Dollar-Cost Averaging, which is an investment
strategy used in the financial markets. DCA involves regularly
investing a fixed amount of money at predetermined intervals,
regardless of the {"asset's"} price.
</QAndAAccordion>
<QAndAAccordion question="Why one should do DCA?">
Recurring swaps (aka DCA) remove the need to time the market,
neutralising the short term market volatility, and helps you
build a portfolio, distributed over a period of time.
</QAndAAccordion>
<QAndAAccordion question="Can I cancel my stacks?">
Yes. You can cancel your stacks anytime. Your funds will be
withdrawn immediately to your wallet. To do it, you have to
connect your wallet, go to your stacks, choose a stack, click
cancel and confirm transaction with your wallet.
</QAndAAccordion>
</div>
</div>
</div>
</section>
<FAQ />
<section className="px-6 mx-auto mb-20 max-w-7xl lg:px-0 md:mb-32">
<TryStacklyBanner />
</section>
Expand Down
1 change: 0 additions & 1 deletion packages/landing/components/QAndAAccordion/index.ts

This file was deleted.

24 changes: 0 additions & 24 deletions packages/landing/components/TryStacklyBanner/TryStacklyBanner.tsx

This file was deleted.

3 changes: 1 addition & 2 deletions packages/landing/components/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,2 @@
export * from "./navbar";
export * from "./QAndAAccordion";
export * from "./TryStacklyBanner";
export * from "./sections";
9 changes: 8 additions & 1 deletion packages/landing/components/navbar/MobileMenu.tsx
Original file line number Diff line number Diff line change
@@ -1,16 +1,21 @@
"use client";

import { useState } from "react";

import Link from "next/link";

import { Button, ButtonLink } from "@/ui";
import { EVENTS } from "@/analytics";
import { STACKLY_APP_URL } from "@/constants";
import { useFathomAnalytics } from "@/contexts";

export default function MobileMenu({
passedThresholdHeight,
}: {
passedThresholdHeight: boolean;
}) {
const [isOpen, setIsOpen] = useState(false);
const { trackClick } = useFathomAnalytics();

const toggle = () => setIsOpen(!isOpen);

Expand All @@ -20,7 +25,9 @@ export default function MobileMenu({
target="_blank"
variant={passedThresholdHeight ? "primary" : "secondary"}
href={STACKLY_APP_URL}
onClick={toggle}
onClick={() => {
trackClick(EVENTS.NAVBAR.MOBILE.LAUNCH_APP_CLICK);
}}
>
Launch app
</ButtonLink>
Expand Down
15 changes: 12 additions & 3 deletions packages/landing/components/navbar/Navbar.tsx
Original file line number Diff line number Diff line change
@@ -1,16 +1,22 @@
"use client";

import { useEffect, useState } from "react";

import Link from "next/link";
import MobileMenu from "./MobileMenu";

import { EVENTS } from "@/analytics";
import { ButtonLink } from "@/ui";
import Logo from "./Logo";
import { STACKLY_APP_URL } from "@/constants";
import { useEffect, useRef, useState } from "react";
import { useFathomAnalytics } from "@/contexts";

import MobileMenu from "./MobileMenu";
import Logo from "./Logo";

const THRESHOLD_HEIGHT = 320;

export function Navbar() {
const [scrollYPos, setScrollYPos] = useState(0);
const { trackClick } = useFathomAnalytics();

useEffect(() => {
const handleScroll = () => {
Expand Down Expand Up @@ -51,6 +57,9 @@ export function Navbar() {
target="_blank"
variant={passedThresholdHeight ? "primary" : "quaternary"}
href={STACKLY_APP_URL}
onClick={() => {
trackClick(EVENTS.NAVBAR.DESKTOP.LAUNCH_APP_CLICK);
}}
>
Launch app
</ButtonLink>
Expand Down
Loading