Skip to content

Commit

Permalink
feat: add analytics
Browse files Browse the repository at this point in the history
  • Loading branch information
Rei-x committed Sep 3, 2024
1 parent ad2f27c commit 76ca245
Show file tree
Hide file tree
Showing 7 changed files with 190 additions and 1 deletion.
6 changes: 5 additions & 1 deletion next.config.mjs
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
/** @type {import('next').NextConfig} */
const nextConfig = {
images: {
domains: ["github.com"],
remotePatterns: [
{
hostname: "github.com",
},
],
},
reactStrictMode: true,
typescript: {
Expand Down
15 changes: 15 additions & 0 deletions src/pages/_app.tsx
Original file line number Diff line number Diff line change
@@ -1,16 +1,31 @@
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
import type { AppProps } from "next/app";
import { Space_Grotesk } from "next/font/google";
import Script from "next/script";

import { cn } from "@/lib/utils";
import "@/styles/globals.css";
import type { UmamiTracker } from "@/types/umami";

const inter = Space_Grotesk({ subsets: ["latin"] });

const queryClient = new QueryClient();

declare global {
interface Window {
umami: UmamiTracker;
}
}

export default function App({ Component, pageProps }: AppProps) {
return (
<QueryClientProvider client={queryClient}>
<Script
defer={true}
src="https://analytics.solvro.pl/script.js"
data-website-id="ab126a0c-c0ab-401b-bf9d-da652aab69ec"
data-domains="planer.solvro.pl"
/>
<div className={cn(inter.className, "min-h-screen")}>
<Component {...pageProps} />
</div>
Expand Down
4 changes: 4 additions & 0 deletions src/pages/createplan/[id].tsx
Original file line number Diff line number Diff line change
Expand Up @@ -252,13 +252,15 @@ const CreatePlan = ({
const inputRef = useRef<HTMLInputElement>(null);

const changePlanName = (newName: string) => {
void window.umami.track("Change plan name");
setPlan({
...plan,
name: newName,
});
};

const checkCourse = (id: string) => {
void window.umami.track("Check course");
setPlan({
...plan,
courses: plan.courses.map((course) =>
Expand All @@ -269,6 +271,7 @@ const CreatePlan = ({
});
};
const checkGroup = (id: string) => {
void window.umami.track("Change group");
setPlan({
...plan,
groups: plan.groups.map((group) =>
Expand Down Expand Up @@ -350,6 +353,7 @@ const CreatePlan = ({
<div className="flex flex-row flex-nowrap items-center justify-between bg-mainbutton3 text-sm text-white">
<Link
href="/plans"
data-umami-event="Back to plans"
className="flex w-1/2 cursor-pointer items-center justify-center gap-2 p-2 font-semibold transition-all hover:bg-solvroshadow hover:shadow-lg"
>
<IoMdArrowBack size={20} className="block" />
Expand Down
2 changes: 2 additions & 0 deletions src/pages/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,7 @@ const JoinUsBlock = () => (
window.location.hostname === "planer.solvro.pl") ? (
<Link
href="/plans"
data-umami-event="Landing - Go to planning"
className={buttonVariants({
size: "lg",
variant: "outline",
Expand All @@ -185,6 +186,7 @@ const JoinUsBlock = () => (
<Button
disabled={true}
variant="outline"
data-umami-event="Landing - incoming soon"
className="h-20 cursor-pointer self-center border-4 text-xl transition-all duration-300 hover:bg-white hover:shadow-[0_0_5px_rgb(200,200,255),0_0_10px_rgb(164,200,255)] md:mt-0 md:p-7"
>
Już niedługo :)
Expand Down
1 change: 1 addition & 0 deletions src/pages/login.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ export default function Home() {

<Link
href="/api/login"
data-umami-event="Login"
className={buttonVariants({
size: "lg",
variant: "outline",
Expand Down
4 changes: 4 additions & 0 deletions src/pages/plans.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,10 @@ const Plans = () => {
id: plans.length + 1,
};

void window.umami.track("Create plan", {
numberOfPlans: plans.length,
});

void router.push(`/createplan/${newPlan.id}`).then(() => {
setPlans([...plans, newPlan]);
});
Expand Down
159 changes: 159 additions & 0 deletions src/types/umami.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,159 @@
interface TrackedProperties {
/**
* Hostname of server
*
* @description extracted from `window.location.hostname`
* @example 'analytics.umami.is'
*/
hostname: string;

/**
* Browser language
*
* @description extracted from `window.navigator.language`
* @example 'en-US', 'fr-FR'
*/
language: string;

/**
* Page referrer
*
* @description extracted from `window.navigator.language`
* @example 'https://analytics.umami.is/docs/getting-started'
*/
referrer: string;

/**
* Screen dimensions
*
* @description extracted from `window.screen.width` and `window.screen.height`
* @example '1920x1080', '2560x1440'
*/
screen: string;

/**
* Page title
*
* @description extracted from `document.querySelector('head > title')`
* @example 'umami'
*/
title: string;

/**
* Page url
*
* @description built from `${window.location.pathname}${window.location.search}`
* @example 'docs/getting-started'
*/
url: string;

/**
* Website ID (required)
*
* @example 'b59e9c65-ae32-47f1-8400-119fcf4861c4'
*/
website: string;
}

type WithRequired<T, K extends keyof T> = T & { [P in K]-?: T[P] };

/**
*
* Event Data can work with any JSON data. There are a few rules in place to maintain performance.
* - Numbers have a max precision of 4.
* - Strings have a max length of 500.
* - Arrays are converted to a String, with the same max length of 500.
* - Objects have a max of 50 properties. Arrays are considered 1 property.
*/
interface EventData {
[key: string]:
| EventData
| EventData[]
| number[]
| string[]
| number
| string;
}

type EventProperties = WithRequired<TrackedProperties, "website"> & {
/**
* NOTE: event names will be truncated past 50 characters
*/
name: string;
data?: EventData;
};
type PageViewProperties = WithRequired<TrackedProperties, "website">;
type CustomEventFunction = (
props: PageViewProperties,
) => EventProperties | PageViewProperties;

export interface UmamiTracker {
track: {
/**
* Track a page view
*
* @example ```
* umami.track();
* ```
*/
(): Promise<string>;

/**
* Track an event with a given name
*
* NOTE: event names will be truncated past 50 characters
*
* @example ```
* umami.track('signup-button');
* ```
*/
// eslint-disable-next-line @typescript-eslint/unified-signatures
(eventName: string): Promise<string>;

/**
* Tracks an event with dynamic data.
*
* NOTE: event names will be truncated past 50 characters
*
* When tracking events, the default properties are included in the payload. This is equivalent to running:
*
* ```js
* umami.track(props => ({
* ...props,
* name: 'signup-button',
* data: {
* name: 'newsletter',
* id: 123
* }
* }));
* ```
*
* @example ```
* umami.track('signup-button', { name: 'newsletter', id: 123 });
* ```
*/
// eslint-disable-next-line @typescript-eslint/unified-signatures
(eventName: string, obj: EventData): Promise<string>;

/**
* Tracks a page view with custom properties
*
* @example ```
* umami.track({ website: 'e676c9b4-11e4-4ef1-a4d7-87001773e9f2', url: '/home', title: 'Home page' });
* ```
*/
// eslint-disable-next-line @typescript-eslint/unified-signatures
(properties: PageViewProperties): Promise<string>;

/**
* Tracks an event with fully customizable dynamic data
* If you don't specify any `name` and/or `data`, it will be treated as a page view
*
* @example ```
* umami.track((props) => ({ ...props, url: path }));
* ```
*/
// eslint-disable-next-line @typescript-eslint/unified-signatures
(eventFunction: CustomEventFunction): Promise<string>;
};
}

0 comments on commit 76ca245

Please sign in to comment.