From 8024f51fb4b8583c1b498290405c77cd176df59c Mon Sep 17 00:00:00 2001 From: Tobias Lins Date: Wed, 26 Apr 2023 08:47:36 +0200 Subject: [PATCH] Add analytics events --- app/layout.tsx | 3 ++- app/product/[handle]/page.tsx | 2 +- components/cart/button.tsx | 2 ++ components/cart/delete-item-button.tsx | 6 ++++++ components/cart/edit-item-quantity-button.tsx | 12 +++++++++++- components/cart/modal.tsx | 8 ++++++++ components/layout/navbar/search.tsx | 5 +++++ components/product/add-to-cart.tsx | 17 +++++++++++++---- package.json | 1 + pnpm-lock.yaml | 10 ++++++++++ 10 files changed, 59 insertions(+), 7 deletions(-) diff --git a/app/layout.tsx b/app/layout.tsx index f1e4769af..4924644aa 100644 --- a/app/layout.tsx +++ b/app/layout.tsx @@ -1,8 +1,8 @@ +import { Analytics } from '@vercel/analytics/react'; import Navbar from 'components/layout/navbar'; import { Inter } from 'next/font/google'; import { ReactNode, Suspense } from 'react'; import './globals.css'; - const { TWITTER_CREATOR, TWITTER_SITE, SITE_NAME } = process.env; export const metadata = { @@ -39,6 +39,7 @@ export default async function RootLayout({ children }: { children: ReactNode })
{children}
+ ); diff --git a/app/product/[handle]/page.tsx b/app/product/[handle]/page.tsx index 612cd0236..8bf09d058 100644 --- a/app/product/[handle]/page.tsx +++ b/app/product/[handle]/page.tsx @@ -68,7 +68,7 @@ export default async function ProductPage({ params }: { params: { handle: string currencyCode={product.priceRange.maxVariantPrice.currencyCode} images={product.images.map((image: Image) => ({ src: image.url, - altText: image.altText + altText: image?.altText }))} /> diff --git a/components/cart/button.tsx b/components/cart/button.tsx index aed87ee7a..d1977fe05 100644 --- a/components/cart/button.tsx +++ b/components/cart/button.tsx @@ -6,6 +6,7 @@ import { useCookies } from 'react-cookie'; import CartIcon from 'components/icons/cart'; import CartModal from './modal'; +import { track } from '@vercel/analytics/react'; import type { Cart } from 'lib/shopify/types'; export default function CartButton({ @@ -53,6 +54,7 @@ export default function CartButton({ aria-label="Open cart" onClick={() => { setCartIsOpen(true); + track('Open Cart', { quantity: cart.totalQuantity, cartId: cart.id }); }} className="relative right-0 top-0" data-testid="open-cart" diff --git a/components/cart/delete-item-button.tsx b/components/cart/delete-item-button.tsx index 789a87754..90c257a4a 100644 --- a/components/cart/delete-item-button.tsx +++ b/components/cart/delete-item-button.tsx @@ -1,3 +1,4 @@ +import { track } from '@vercel/analytics'; import CloseIcon from 'components/icons/close'; import LoadingDots from 'components/loading-dots'; import { useRouter } from 'next/navigation'; @@ -21,6 +22,11 @@ export default function DeleteItemButton({ item }: { item: CartItem }) { }); const data = await response.json(); + track('Remove From Cart', { + merchandiseId: item.id, + quantity: item.quantity + }); + if (data.error) { alert(data.error); return; diff --git a/components/cart/edit-item-quantity-button.tsx b/components/cart/edit-item-quantity-button.tsx index 2249bd1aa..b60a885ce 100644 --- a/components/cart/edit-item-quantity-button.tsx +++ b/components/cart/edit-item-quantity-button.tsx @@ -1,6 +1,7 @@ import { useRouter } from 'next/navigation'; import { startTransition, useState } from 'react'; +import { track } from '@vercel/analytics'; import clsx from 'clsx'; import MinusIcon from 'components/icons/minus'; import PlusIcon from 'components/icons/plus'; @@ -20,17 +21,26 @@ export default function EditItemQuantityButton({ async function handleEdit() { setEditing(true); + const quantity = type === 'plus' ? item.quantity + 1 : item.quantity - 1; + const response = await fetch(`/api/cart`, { method: type === 'minus' && item.quantity - 1 === 0 ? 'DELETE' : 'PUT', body: JSON.stringify({ lineId: item.id, variantId: item.merchandise.id, - quantity: type === 'plus' ? item.quantity + 1 : item.quantity - 1 + quantity }) }); const data = await response.json(); + track('Change Item Quantity', { + merchandiseId: item.id, + quantity, + type: type === 'plus' ? 'increase' : 'decrease', + name: item.merchandise.title + }); + if (data.error) { alert(data.error); return; diff --git a/components/cart/modal.tsx b/components/cart/modal.tsx index 4220cfe94..ebd7acec3 100644 --- a/components/cart/modal.tsx +++ b/components/cart/modal.tsx @@ -3,6 +3,7 @@ import { AnimatePresence, motion } from 'framer-motion'; import Image from 'next/image'; import Link from 'next/link'; +import { track } from '@vercel/analytics'; import CloseIcon from 'components/icons/close'; import ShoppingBagIcon from 'components/icons/shopping-bag'; import Price from 'components/price'; @@ -172,6 +173,13 @@ export default function CartModal({ { + track('Checkout', { + cartId: cart.id, + cartTotal: cart.cost.totalAmount.amount, + cartCurrency: cart.cost.totalAmount.currencyCode + }); + }} className="flex w-full items-center justify-center bg-black p-3 text-sm font-medium uppercase text-white opacity-90 hover:opacity-100 dark:bg-white dark:text-black" > Proceed to Checkout diff --git a/components/layout/navbar/search.tsx b/components/layout/navbar/search.tsx index b3ff9a6bb..f4538ab00 100644 --- a/components/layout/navbar/search.tsx +++ b/components/layout/navbar/search.tsx @@ -2,6 +2,7 @@ import { useRouter, useSearchParams } from 'next/navigation'; +import { track } from '@vercel/analytics'; import SearchIcon from 'components/icons/search'; export default function Search() { @@ -16,6 +17,10 @@ export default function Search() { if (search.value) { router.push(`/search?q=${search.value}`); + + track('Search', { + query: search.value + }); } else { router.push(`/search`); } diff --git a/components/product/add-to-cart.tsx b/components/product/add-to-cart.tsx index 9da11bafa..128a3a6b6 100644 --- a/components/product/add-to-cart.tsx +++ b/components/product/add-to-cart.tsx @@ -1,5 +1,6 @@ 'use client'; +import { track } from '@vercel/analytics'; import clsx from 'clsx'; import { useRouter, useSearchParams } from 'next/navigation'; import { useEffect, useState, useTransition } from 'react'; @@ -14,7 +15,7 @@ export function AddToCart({ variants: ProductVariant[]; availableForSale: boolean; }) { - const [selectedVariantId, setSelectedVariantId] = useState(variants[0]?.id); + const [selectedVariant, setSelectedVariant] = useState(variants[0]); const router = useRouter(); const searchParams = useSearchParams(); const [isPending, startTransition] = useTransition(); @@ -28,9 +29,9 @@ export function AddToCart({ ); if (variant) { - setSelectedVariantId(variant.id); + setSelectedVariant(variant); } - }, [searchParams, variants, setSelectedVariantId]); + }, [searchParams, variants, setSelectedVariant]); const isMutating = adding || isPending; @@ -42,10 +43,18 @@ export function AddToCart({ const response = await fetch(`/api/cart`, { method: 'POST', body: JSON.stringify({ - merchandiseId: selectedVariantId + merchandiseId: selectedVariant?.id }) }); + track('Add To Cart', { + merchandiseId: selectedVariant?.id || null, + name: selectedVariant?.title || null, + price: selectedVariant?.price.amount || null, + currency: selectedVariant?.price.currencyCode || null, + quantity: 1 + }); + const data = await response.json(); if (data.error) { diff --git a/package.json b/package.json index 95888c9fd..3cc3f3dd6 100644 --- a/package.json +++ b/package.json @@ -23,6 +23,7 @@ }, "dependencies": { "@headlessui/react": "^1.7.10", + "@vercel/analytics": "^1.0.0", "@vercel/og": "^0.1.0", "clsx": "^1.2.1", "framer-motion": "^8.4.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 0ca1eb68a..6bb68688a 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -7,6 +7,7 @@ specifiers: '@types/node': 18.13.0 '@types/react': 18.0.27 '@types/react-dom': 18.0.10 + '@vercel/analytics': ^1.0.0 '@vercel/git-hooks': ^1.0.0 '@vercel/og': ^0.1.0 autoprefixer: ^10.4.13 @@ -30,6 +31,7 @@ specifiers: dependencies: '@headlessui/react': 1.7.14_biqbaboplfbrettd7655fr4n2y + '@vercel/analytics': 1.0.0_react@18.2.0 '@vercel/og': 0.1.0 clsx: 1.2.1 framer-motion: 8.5.5_biqbaboplfbrettd7655fr4n2y @@ -526,6 +528,14 @@ packages: eslint-visitor-keys: 3.4.0 dev: true + /@vercel/analytics/1.0.0_react@18.2.0: + resolution: {integrity: sha512-RQmj7pv82JwGDHrnKeRc6TtSw2U7rWNubc2IH0ernTzWTj02yr9zvIYiYJeztsBzrJtWv7m8Nz6vxxb+cdEtJw==} + peerDependencies: + react: ^16.8||^17||^18 + dependencies: + react: 18.2.0 + dev: false + /@vercel/git-hooks/1.0.0: resolution: {integrity: sha512-OxDFAAdyiJ/H0b8zR9rFCu3BIb78LekBXOphOYG3snV4ULhKFX387pBPpqZ9HLiRTejBWBxYEahkw79tuIgdAA==} requiresBuild: true