mirror of
https://github.com/mfts/papermark.git
synced 2025-12-20 01:03:24 +08:00
feat: add gtm
This commit is contained in:
@@ -1,6 +1,5 @@
|
||||
"use client";
|
||||
|
||||
import { Metadata } from "next";
|
||||
import Link from "next/link";
|
||||
import { useParams } from "next/navigation";
|
||||
|
||||
@@ -145,9 +144,7 @@ export default function Login() {
|
||||
signIn("google", {
|
||||
...(next && next.length > 0 ? { callbackUrl: next } : {}),
|
||||
}).then((res) => {
|
||||
if (res?.status) {
|
||||
setClickedMethod(undefined);
|
||||
}
|
||||
setClickedMethod(undefined);
|
||||
});
|
||||
}}
|
||||
loading={clickedMethod === "google"}
|
||||
@@ -169,9 +166,7 @@ export default function Login() {
|
||||
signIn("linkedin", {
|
||||
...(next && next.length > 0 ? { callbackUrl: next } : {}),
|
||||
}).then((res) => {
|
||||
if (res?.status) {
|
||||
setClickedMethod(undefined);
|
||||
}
|
||||
setClickedMethod(undefined);
|
||||
});
|
||||
}}
|
||||
loading={clickedMethod === "linkedin"}
|
||||
@@ -192,6 +187,8 @@ export default function Login() {
|
||||
setClickedMethod("passkey");
|
||||
signInWithPasskey({
|
||||
tenantId: process.env.NEXT_PUBLIC_HANKO_TENANT_ID as string,
|
||||
}).then(() => {
|
||||
setClickedMethod(undefined);
|
||||
});
|
||||
}}
|
||||
variant="outline"
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
import { Metadata } from "next";
|
||||
|
||||
import { GTMComponent } from "@/components/gtm-component";
|
||||
|
||||
import LoginClient from "./page-client";
|
||||
|
||||
const data = {
|
||||
@@ -37,5 +39,10 @@ export const metadata: Metadata = {
|
||||
};
|
||||
|
||||
export default function LoginPage() {
|
||||
return <LoginClient />;
|
||||
return (
|
||||
<>
|
||||
<GTMComponent />
|
||||
<LoginClient />
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
11
components/gtm-component.tsx
Normal file
11
components/gtm-component.tsx
Normal file
@@ -0,0 +1,11 @@
|
||||
import { GoogleTagManager } from "@next/third-parties/google";
|
||||
|
||||
const GTM_ID = process.env.NEXT_PUBLIC_GTM_ID;
|
||||
|
||||
export function GTMComponent() {
|
||||
if (!GTM_ID) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return <GoogleTagManager gtmId={GTM_ID} />;
|
||||
}
|
||||
20
package-lock.json
generated
20
package-lock.json
generated
@@ -21,6 +21,7 @@
|
||||
"@github/webauthn-json": "^2.1.1",
|
||||
"@jitsu/js": "^1.10.3",
|
||||
"@next-auth/prisma-adapter": "^1.0.7",
|
||||
"@next/third-parties": "^15.4.4",
|
||||
"@pdf-lib/fontkit": "^1.1.1",
|
||||
"@prisma/client": "^6.5.0",
|
||||
"@radix-ui/react-accordion": "^1.2.11",
|
||||
@@ -2936,6 +2937,19 @@
|
||||
"node": ">= 10"
|
||||
}
|
||||
},
|
||||
"node_modules/@next/third-parties": {
|
||||
"version": "15.4.4",
|
||||
"resolved": "https://registry.npmjs.org/@next/third-parties/-/third-parties-15.4.4.tgz",
|
||||
"integrity": "sha512-0GIEDSxfl9hTU4Ne1TVr/lZUS1wGhDPB4gt6TNvLBA3Fioum+qZRoPt3fiOW9hHQynyLsojglRDaB97T92h0Og==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"third-party-capital": "1.0.20"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"next": "^13.0.0 || ^14.0.0 || ^15.0.0",
|
||||
"react": "^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@nodelib/fs.scandir": {
|
||||
"version": "2.1.5",
|
||||
"resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz",
|
||||
@@ -16216,6 +16230,12 @@
|
||||
"node": ">=0.8"
|
||||
}
|
||||
},
|
||||
"node_modules/third-party-capital": {
|
||||
"version": "1.0.20",
|
||||
"resolved": "https://registry.npmjs.org/third-party-capital/-/third-party-capital-1.0.20.tgz",
|
||||
"integrity": "sha512-oB7yIimd8SuGptespDAZnNkzIz+NWaJCu2RMsbs4Wmp9zSDUM8Nhi3s2OOcqYuv3mN4hitXc8DVx+LyUmbUDiA==",
|
||||
"license": "ISC"
|
||||
},
|
||||
"node_modules/throttleit": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/throttleit/-/throttleit-2.1.0.tgz",
|
||||
|
||||
@@ -32,6 +32,7 @@
|
||||
"@github/webauthn-json": "^2.1.1",
|
||||
"@jitsu/js": "^1.10.3",
|
||||
"@next-auth/prisma-adapter": "^1.0.7",
|
||||
"@next/third-parties": "^15.4.4",
|
||||
"@pdf-lib/fontkit": "^1.1.1",
|
||||
"@prisma/client": "^6.5.0",
|
||||
"@radix-ui/react-accordion": "^1.2.11",
|
||||
|
||||
@@ -70,7 +70,7 @@ export const authOptions: NextAuthOptions = {
|
||||
const mainDomainObj = new URL(mainDomainUrl);
|
||||
urlObj.hostname = mainDomainObj.hostname;
|
||||
urlObj.protocol = mainDomainObj.protocol;
|
||||
urlObj.port = mainDomainObj.port || '';
|
||||
urlObj.port = mainDomainObj.port || "";
|
||||
|
||||
finalUrl = urlObj.toString();
|
||||
}
|
||||
|
||||
@@ -3,15 +3,17 @@ import { useRouter } from "next/router";
|
||||
import { useEffect } from "react";
|
||||
|
||||
import { useTeam } from "@/context/team-context";
|
||||
import { sendGTMEvent } from "@next/third-parties/google";
|
||||
import { toast } from "sonner";
|
||||
|
||||
import UpgradePlanContainer from "@/components/billing/upgrade-plan-container";
|
||||
import AppLayout from "@/components/layouts/app";
|
||||
import { SettingsHeader } from "@/components/settings/settings-header";
|
||||
|
||||
import { useAnalytics } from "@/lib/analytics";
|
||||
import { usePlan } from "@/lib/swr/use-billing";
|
||||
|
||||
import UpgradePlanContainer from "@/components/billing/upgrade-plan-container";
|
||||
import { GTMComponent } from "@/components/gtm-component";
|
||||
import AppLayout from "@/components/layouts/app";
|
||||
import { SettingsHeader } from "@/components/settings/settings-header";
|
||||
|
||||
export default function Billing() {
|
||||
const router = useRouter();
|
||||
const analytics = useAnalytics();
|
||||
@@ -29,6 +31,8 @@ export default function Billing() {
|
||||
teamId: teamId,
|
||||
$set: { teamId: teamId, teamPlan: plan },
|
||||
});
|
||||
|
||||
sendGTMEvent({ event: "upgraded" });
|
||||
}
|
||||
|
||||
if (router.query.cancel) {
|
||||
@@ -39,12 +43,15 @@ export default function Billing() {
|
||||
}, [router.query]);
|
||||
|
||||
return (
|
||||
<AppLayout>
|
||||
<main className="relative mx-2 mb-10 mt-4 space-y-8 overflow-hidden px-1 sm:mx-3 md:mx-5 md:mt-5 lg:mx-7 lg:mt-8 xl:mx-10">
|
||||
<SettingsHeader />
|
||||
<>
|
||||
<GTMComponent />
|
||||
<AppLayout>
|
||||
<main className="relative mx-2 mb-10 mt-4 space-y-8 overflow-hidden px-1 sm:mx-3 md:mx-5 md:mt-5 lg:mx-7 lg:mt-8 xl:mx-10">
|
||||
<SettingsHeader />
|
||||
|
||||
<UpgradePlanContainer />
|
||||
</main>
|
||||
</AppLayout>
|
||||
<UpgradePlanContainer />
|
||||
</main>
|
||||
</AppLayout>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -3,11 +3,15 @@ import { useRouter } from "next/router";
|
||||
import { useEffect } from "react";
|
||||
import { useState } from "react";
|
||||
|
||||
import { sendGTMEvent } from "@next/third-parties/google";
|
||||
import { ArrowLeft as ArrowLeftIcon } from "lucide-react";
|
||||
import { AnimatePresence } from "motion/react";
|
||||
import { useSession } from "next-auth/react";
|
||||
|
||||
import { CustomUser } from "@/lib/types";
|
||||
import { cn } from "@/lib/utils";
|
||||
|
||||
import { GTMComponent } from "@/components/gtm-component";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import Dataroom from "@/components/welcome/dataroom";
|
||||
import DataroomTrial from "@/components/welcome/dataroom-trial";
|
||||
@@ -21,6 +25,7 @@ import Upload from "@/components/welcome/upload";
|
||||
export default function Welcome() {
|
||||
const router = useRouter();
|
||||
const [showSkipButtons, setShowSkipButtons] = useState(false);
|
||||
const { data: session } = useSession();
|
||||
|
||||
useEffect(() => {
|
||||
const timer = setTimeout(() => {
|
||||
@@ -30,6 +35,19 @@ export default function Welcome() {
|
||||
return () => clearTimeout(timer);
|
||||
}, []);
|
||||
|
||||
// Track signup for new users when welcome page loads
|
||||
useEffect(() => {
|
||||
const user = session?.user as CustomUser;
|
||||
if (user?.createdAt) {
|
||||
// Check if user was created within the last 30 seconds (indicating new signup)
|
||||
const isNewUser = new Date(user.createdAt).getTime() > Date.now() - 30000;
|
||||
|
||||
if (isNewUser) {
|
||||
sendGTMEvent({ event: "signup" });
|
||||
}
|
||||
}
|
||||
}, [session]);
|
||||
|
||||
const isDataroomUpload = router.query.type === "dataroom-upload";
|
||||
|
||||
const skipButtonText = isDataroomUpload
|
||||
@@ -41,50 +59,54 @@ export default function Welcome() {
|
||||
: "/documents";
|
||||
|
||||
return (
|
||||
<div className="mx-auto flex h-screen max-w-3xl flex-col items-center justify-center overflow-x-hidden">
|
||||
<AnimatePresence mode="wait">
|
||||
{router.query.type ? (
|
||||
<>
|
||||
<button
|
||||
className="group absolute left-2 top-10 z-40 rounded-full p-2 transition-all hover:bg-gray-400 sm:left-10"
|
||||
onClick={() => router.back()}
|
||||
>
|
||||
<ArrowLeftIcon className="h-8 w-8 text-gray-500 group-hover:text-gray-800 group-active:scale-90" />
|
||||
</button>
|
||||
<>
|
||||
<GTMComponent />
|
||||
<div className="mx-auto flex h-screen max-w-3xl flex-col items-center justify-center overflow-x-hidden">
|
||||
<AnimatePresence mode="wait">
|
||||
{router.query.type ? (
|
||||
<>
|
||||
<button
|
||||
className="group absolute left-2 top-10 z-40 rounded-full p-2 transition-all hover:bg-gray-400 sm:left-10"
|
||||
onClick={() => router.back()}
|
||||
>
|
||||
<ArrowLeftIcon className="h-8 w-8 text-gray-500 group-hover:text-gray-800 group-active:scale-90" />
|
||||
</button>
|
||||
|
||||
<Button
|
||||
variant={"link"}
|
||||
onClick={() => router.push(skipButtonPath)}
|
||||
className={cn(
|
||||
"absolute right-2 top-10 z-40 p-2 text-muted-foreground sm:right-10",
|
||||
showSkipButtons ? "block" : "hidden",
|
||||
)}
|
||||
>
|
||||
{skipButtonText}
|
||||
</Button>
|
||||
</>
|
||||
) : (
|
||||
<Intro key="intro" />
|
||||
)}
|
||||
{router.query.type === "next" && <Next key="next" />}
|
||||
{router.query.type === "select" && <Select key="select" />}
|
||||
{router.query.type === "pitchdeck" && <Upload key="pitchdeck" />}
|
||||
{router.query.type === "document" && <Upload key="document" />}
|
||||
{router.query.type === "sales-document" && (
|
||||
<Upload key="sales-document" />
|
||||
)}
|
||||
{router.query.type === "notion" && <NotionForm key="notion" />}
|
||||
{router.query.type === "dataroom" && <Dataroom key="dataroom" />}
|
||||
{router.query.type === "dataroom-trial" && (
|
||||
<DataroomTrial key="dataroom-trial" />
|
||||
)}
|
||||
{router.query.type === "dataroom-upload" && router.query.dataroomId && (
|
||||
<DataroomUpload
|
||||
key="dataroom-upload"
|
||||
dataroomId={router.query.dataroomId as string}
|
||||
/>
|
||||
)}
|
||||
</AnimatePresence>
|
||||
</div>
|
||||
<Button
|
||||
variant={"link"}
|
||||
onClick={() => router.push(skipButtonPath)}
|
||||
className={cn(
|
||||
"absolute right-2 top-10 z-40 p-2 text-muted-foreground sm:right-10",
|
||||
showSkipButtons ? "block" : "hidden",
|
||||
)}
|
||||
>
|
||||
{skipButtonText}
|
||||
</Button>
|
||||
</>
|
||||
) : (
|
||||
<Intro key="intro" />
|
||||
)}
|
||||
{router.query.type === "next" && <Next key="next" />}
|
||||
{router.query.type === "select" && <Select key="select" />}
|
||||
{router.query.type === "pitchdeck" && <Upload key="pitchdeck" />}
|
||||
{router.query.type === "document" && <Upload key="document" />}
|
||||
{router.query.type === "sales-document" && (
|
||||
<Upload key="sales-document" />
|
||||
)}
|
||||
{router.query.type === "notion" && <NotionForm key="notion" />}
|
||||
{router.query.type === "dataroom" && <Dataroom key="dataroom" />}
|
||||
{router.query.type === "dataroom-trial" && (
|
||||
<DataroomTrial key="dataroom-trial" />
|
||||
)}
|
||||
{router.query.type === "dataroom-upload" &&
|
||||
router.query.dataroomId && (
|
||||
<DataroomUpload
|
||||
key="dataroom-upload"
|
||||
dataroomId={router.query.dataroomId as string}
|
||||
/>
|
||||
)}
|
||||
</AnimatePresence>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user