Skip to content

Commit

Permalink
Merge pull request #164
Browse files Browse the repository at this point in the history
feat: google analytics
  • Loading branch information
johnshift authored Nov 13, 2024
2 parents 7a5c1d5 + 3816b71 commit 71f5fc7
Show file tree
Hide file tree
Showing 16 changed files with 179 additions and 32 deletions.
1 change: 1 addition & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ ENV NEXT_PUBLIC_MW_URL=https://middleware.ecosystem.vision
ENV NEXT_PUBLIC_FRONTEND_URL=https://ecosystem.vision

ENV NEXT_PUBLIC_PAGE_SIZE=20
ENV NEXT_PUBLIC_GOOGLE_ANALYTICS_ID=G-9ZY0R2T7KR
RUN pnpm build

# Production image, copy all the files and run next
Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
},
"dependencies": {
"@next/bundle-analyzer": "^14.2.5",
"@next/third-parties": "^15.0.3",
"@nextui-org/react": "^2.3.6",
"@sentry/nextjs": "^8",
"@splinetool/react-spline": "^4.0.0",
Expand Down
20 changes: 20 additions & 0 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 5 additions & 0 deletions src/app/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@ import './swiper.css';
import type { Metadata } from 'next';
import { Inter } from 'next/font/google';

import { GoogleAnalytics } from '@next/third-parties/google';

import { GOOGLE_ANALYTICS_ID } from '@/shared/core/envs';
import { grotesk, interTight } from '@/shared/core/fonts';
import { InitPathSyncer } from '@/shared/components/init-path-syncer';
import { NavLayout } from '@/shared/components/nav-space-layout';
Expand Down Expand Up @@ -43,6 +46,8 @@ const RootLayout: React.FC<RootLayoutProps> = ({ children }) => (
<PageScrollDisabler />
<InitPathSyncer />
<Toaster />

<GoogleAnalytics gaId={GOOGLE_ANALYTICS_ID ?? ''} />
</body>
</html>
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,11 @@
import { useState } from 'react';
import { createPortal } from 'react-dom';

import { sendGAEvent } from '@next/third-parties/google';
import { Button, Textarea } from '@nextui-org/react';
import { useAtom, useAtomValue } from 'jotai';

import { GA_EVENT } from '@/shared/core/constants';
import { useIsMounted } from '@/shared/hooks/use-is-mounted';
import { useIsDesktop } from '@/shared/hooks/use-media-query';
import { AiIcon } from '@/shared/components/icons/ai-icon';
Expand Down Expand Up @@ -36,6 +38,9 @@ export const AiGrantProgramFinderPortal = () => {
};

const onFind = () => {
sendGAEvent('event', GA_EVENT.GRANTS.AI_GRANT_PROGRAM_FINDER_SUBMIT, {
value: aiQueryInput,
});
setAiQuery(aiQueryInput);
};

Expand Down
7 changes: 6 additions & 1 deletion src/grants/components/grant-card/full-grant-card.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { Avatar } from '@nextui-org/react';

import { GA_EVENT } from '@/shared/core/constants';
import { cn } from '@/shared/utils/cn';
import { getLogoUrl } from '@/shared/utils/get-logo-url';

Expand Down Expand Up @@ -87,7 +88,11 @@ export const FullGrantCard = ({ grant }: Props) => {
name={name}
/>
</div>
<ApplyButton url={url} />
<ApplyButton
url={url}
gaEvent={GA_EVENT.GRANTS.VIEW_PROGRAM}
value={slug}
/>
</div>
</div>
);
Expand Down
2 changes: 1 addition & 1 deletion src/grants/components/grant-list/ai-grant-list.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ export const AiGrantList = () => {
<VirtualWrapper count={grants.length}>
{(index) => (
<div className="pt-6 lg:pt-8">
<GrantListItem grant={grants[index]} />
<GrantListItem isAiResult grant={grants[index]} />
</div>
)}
</VirtualWrapper>
Expand Down
13 changes: 12 additions & 1 deletion src/grants/components/grant-list/apply-button.tsx
Original file line number Diff line number Diff line change
@@ -1,24 +1,35 @@
'use client';

import { sendGAEvent } from '@next/third-parties/google';
import { Button } from '@nextui-org/react';

import { openNewTab } from '@/shared/utils/open-new-tab';

interface Props {
url: string | null;
gaEvent: string;
value: string;
text?: string;
}

/**
* Need to implement this as button (instead of link) to avoid nested links
* Parent card is already a link, so it'll throw react minification error
*/
export const ApplyButton = ({ url, text = 'View Program' }: Props) => {
export const ApplyButton = ({
url,
gaEvent,
value,
text = 'View Program',
}: Props) => {
if (!url) return null;

const onClick: React.MouseEventHandler = (e) => {
e.preventDefault();
e.stopPropagation();

sendGAEvent('event', gaEvent, { value });

openNewTab(url);
};

Expand Down
36 changes: 36 additions & 0 deletions src/grants/components/grant-list/grant-list-item-link-wrapper.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
'use client';

import Link from 'next/link';
import React from 'react';

import { sendGAEvent } from '@next/third-parties/google';

import { GA_EVENT, ROUTE_SECTIONS } from '@/shared/core/constants';

import { GRANT_TEST_IDS } from '@/grants/core/constants';

interface Props {
slug: string;
children: React.ReactNode;
}

export const GrantListItemLinkWrapper = ({ slug, children }: Props) => {
const sendAnalytics = () => {
sendGAEvent('event', GA_EVENT.GRANTS.GRANT_ITEM_CLICK, {
value: slug,
});
};

return (
<Link
prefetch
href={`/${ROUTE_SECTIONS.GRANT_IMPACT}/${slug}`}
className="flex flex-wrap items-center justify-between rounded-2xl bg-gradient-to-r from-[#191919] to-[#0D0D0D] p-4 text-13 text-white transition-all duration-300 md:p-5 lg:flex-nowrap"
data-uuid={slug}
data-testid={GRANT_TEST_IDS.GRANT_ITEM}
onClick={sendAnalytics}
>
{children}
</Link>
);
};
47 changes: 20 additions & 27 deletions src/grants/components/grant-list/grant-list-item.tsx
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
import Link from 'next/link';
import { Avatar } from '@nextui-org/react';

import { Avatar, Button } from '@nextui-org/react';

import { ROUTE_SECTIONS } from '@/shared/core/constants';
import { GA_EVENT } from '@/shared/core/constants';
import { cn } from '@/shared/utils/cn';
import { getLogoUrl } from '@/shared/utils/get-logo-url';

import { GRANT_TEST_IDS } from '@/grants/core/constants';
import { Grant } from '@/grants/core/schemas';
import { getGrantCardData } from '@/grants/utils/get-grant-card-data';
import { ApplyButton } from '@/grants/components/grant-list/apply-button';
import { GrantListItemLinkWrapper } from '@/grants/components/grant-list/grant-list-item-link-wrapper';
import { ViewImpactButton } from '@/grants/components/grant-list/view-impact-button';
import { DetailItems } from '@/grants/components/ui/base/detail-item';
import { Title } from '@/grants/components/ui/base/title';
import { WebLinks } from '@/grants/components/ui/base/web-links/web-links';
Expand All @@ -19,11 +19,15 @@ interface Props {
grant: Grant;
isLink?: boolean;
ctaText?: string;
isAiResult?: boolean;
}

export const GrantListItem = ({ grant, isLink = true, ctaText }: Props) => {
// TODO: JOB-679

export const GrantListItem = ({
grant,
isLink = true,
ctaText,
isAiResult,
}: Props) => {
const {
slug,
logo,
Expand All @@ -39,6 +43,12 @@ export const GrantListItem = ({ grant, isLink = true, ctaText }: Props) => {
hasWebLinks,
} = getGrantCardData(grant);

const gaEvent = ctaText
? isAiResult
? GA_EVENT.GRANTS.APPLY_AI_ACTIVE_GRANT
: GA_EVENT.GRANTS.APPLY_ACTIVE_GRANT
: GA_EVENT.GRANTS.VIEW_PROGRAM;

const wrapperClassName =
'flex flex-wrap items-center justify-between rounded-2xl bg-gradient-to-r from-[#191919] to-[#0D0D0D] p-4 text-13 text-white transition-all duration-300 md:p-5 lg:flex-nowrap';

Expand Down Expand Up @@ -96,18 +106,9 @@ export const GrantListItem = ({ grant, isLink = true, ctaText }: Props) => {
</div>
</div>
<div className="flex w-full flex-col items-center justify-end gap-4 pt-6 md:flex-row lg:max-w-[180px] lg:pt-0">
<ApplyButton url={url} text={ctaText} />
<ApplyButton url={url} text={ctaText} gaEvent={gaEvent} value={slug} />

{!ctaText && (
<div className="flex w-full lg:hidden">
<Button
className="mx-auto w-full rounded-xl border border-white/20 font-semibold"
variant="bordered"
>
<span>View Impact</span>
</Button>
</div>
)}
{!ctaText && <ViewImpactButton slug={slug} />}

<div className="hidden lg:flex">
<CaretRightIcon />
Expand All @@ -118,15 +119,7 @@ export const GrantListItem = ({ grant, isLink = true, ctaText }: Props) => {

if (isLink) {
return (
<Link
prefetch
href={`/${ROUTE_SECTIONS.GRANT_IMPACT}/${slug}`}
className={wrapperClassName}
data-uuid={slug}
data-testid={GRANT_TEST_IDS.GRANT_ITEM}
>
{content}
</Link>
<GrantListItemLinkWrapper slug={slug}>{content}</GrantListItemLinkWrapper>
);
}

Expand Down
28 changes: 28 additions & 0 deletions src/grants/components/grant-list/view-impact-button.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
'use client';

import { sendGAEvent } from '@next/third-parties/google';
import { Button } from '@nextui-org/react';

import { GA_EVENT } from '@/shared/core/constants';

interface Props {
slug: string;
}

export const ViewImpactButton = ({ slug }: Props) => {
const sendAnalytics = () => {
sendGAEvent('event', GA_EVENT.GRANTS.VIEW_IMPACT, { value: slug });
};

return (
<div className="flex w-full lg:hidden">
<Button
className="mx-auto w-full rounded-xl border border-white/20 font-semibold"
variant="bordered"
onClick={sendAnalytics}
>
<span>View Impact</span>
</Button>
</div>
);
};
11 changes: 10 additions & 1 deletion src/grants/components/grantee-list/item/client-wrapper.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,9 @@
import Link from 'next/link';
import { useParams } from 'next/navigation';

import { ROUTE_SECTIONS } from '@/shared/core/constants';
import { sendGAEvent } from '@next/third-parties/google';

import { GA_EVENT, ROUTE_SECTIONS } from '@/shared/core/constants';
import { cn } from '@/shared/utils/cn';

import { GRANT_TEST_IDS } from '@/grants/core/constants';
Expand All @@ -26,6 +28,12 @@ export const ClientWrapper = ({
const href = `/${ROUTE_SECTIONS.GRANT_IMPACT}/${params.grantId}/grantees/${granteeId}`;
const isActive = isActiveBypass || params.granteeId === granteeId;

const sendAnalytics = () => {
sendGAEvent('event', GA_EVENT.GRANTS.GRANTEE_ITEM_CLICK, {
value: granteeId,
});
};

return (
<Link
href={href}
Expand All @@ -40,6 +48,7 @@ export const ClientWrapper = ({
},
className,
)}
onClick={sendAnalytics}
{...props}
>
{children}
Expand Down
10 changes: 10 additions & 0 deletions src/grants/components/project-selections/project-selection.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@
import Link from 'next/link';
import { useParams } from 'next/navigation';

import { sendGAEvent } from '@next/third-parties/google';

import { GA_EVENT } from '@/shared/core/constants';
import { cn } from '@/shared/utils/cn';

interface Props {
Expand All @@ -22,6 +25,12 @@ export const ProjectSelection = ({

const isActive = isActiveBypass || paramsProjectId === projectId;

const sendAnalytics = () => {
sendGAEvent('event', GA_EVENT.GRANTS.GRANTEE_PROJECT_SELECTION, {
value: name,
});
};

return (
<Link
prefetch
Expand All @@ -34,6 +43,7 @@ export const ProjectSelection = ({
'is-active': isActive,
},
)}
onClick={sendAnalytics}
>
{name}
</Link>
Expand Down
Loading

0 comments on commit 71f5fc7

Please sign in to comment.