Skip to content

Commit 16bfb75

Browse files
authored
Merge pull request #120 from Team-Senifit/MLFM-162-GTM
[ ] GTM 이벤트 연결
2 parents 3b118f2 + 013a084 commit 16bfb75

File tree

16 files changed

+102
-7
lines changed

16 files changed

+102
-7
lines changed

src/apis/axiosClient.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import axios from "axios";
22
import { AuthError } from "./errors";
33

44
export const axiosClient = axios.create({
5-
baseURL: "/api",
5+
baseURL: "",
66
withCredentials: true, // 쿠키 인증이면 필수
77
});
88

src/apis/createAxiosServer.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,9 @@ import { headers as nextHeaders } from "next/headers";
55
import { AuthError } from "./errors";
66

77
const API_PREFIX = normalizePrefix(process.env.NEXT_PUBLIC_API_BASE ?? "/api");
8-
const API_BASE_URL = (process.env.NEXT_PUBLIC_API_URL || "").trim().replace(/\/+$/, "");
8+
const API_BASE_URL = (process.env.NEXT_PUBLIC_API_URL || "")
9+
.trim()
10+
.replace(/\/+$/, "");
911
const ENV_SITE_URL = ensureOrigin(process.env.NEXT_PUBLIC_SITE_URL);
1012

1113
const devHttpsAgent =

src/app/(plain)/exercise/class/[id]/page.tsx

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,24 +5,29 @@ import useProgramStore from "@/states/useProgramStore";
55
import WorkoutVideoPlaylist from "./panel/WorkoutVideoPlayer";
66
import { useRouter } from "next/navigation";
77
import { useClientReady } from "@/hooks/useClientReady";
8+
import { getGtmClassType, pushGtmEvent } from "@/utils/gtm";
89
// import { useDeadlineTrigger } from "@/hooks/useDeadlineTrigger";
910
// import dayjs from "dayjs";
1011
// import { useToastStore } from "@/states/useToastStore";
1112

1213
const Page = () => {
1314
const isClientReady = useClientReady();
1415
const router = useRouter();
15-
const { selectedProgram } = useProgramStore();
16+
const { selectedProgram, type } = useProgramStore();
1617

1718
// const { setToastOpen } = useToastStore();
1819

20+
const startFired = React.useRef(false);
1921
useEffect(() => {
2022
if (!isClientReady) return;
2123
else if (!selectedProgram) {
2224
router.push("/");
25+
} else if (!startFired.current) {
26+
pushGtmEvent("click_Start", getGtmClassType(type));
27+
startFired.current = true;
2328
}
2429
return () => {};
25-
}, [selectedProgram, router, isClientReady]);
30+
}, [selectedProgram, router, isClientReady, type]);
2631

2732
if (!selectedProgram) return null;
2833

@@ -40,6 +45,7 @@ const Page = () => {
4045
duration={selectedProgram?.duration}
4146
videos={selectedProgram?.videos}
4247
initialId={selectedProgram?.videos[0]?.id}
48+
type={type}
4349
/>
4450
);
4551
};

src/app/(plain)/exercise/class/[id]/panel/Header.tsx

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,17 +5,21 @@ import React, { useState } from "react";
55
import Timer from "./Timer";
66
import useMedia from "@/hooks/useMedia";
77
import SenifitDialog from "@/components/SenifitDialog";
8+
import { WorkoutKind } from "@/types/IRoutine";
9+
import { getGtmClassType, pushGtmEvent } from "@/utils/gtm";
810

911
const Header = ({
1012
seconds,
1113
duration,
1214
onEnd,
1315
isEnd,
16+
type,
1417
}: {
1518
duration: number;
1619
isEnd: boolean;
1720
onEnd: () => void;
1821
seconds: number;
22+
type: "customized" | "popular" | ["thematic", WorkoutKind] | null;
1923
}) => {
2024
const [openDialog, setOpenDialog] = useState(false);
2125

@@ -25,6 +29,7 @@ const Header = ({
2529
if (isEnd) {
2630
onEnd();
2731
} else {
32+
pushGtmEvent("click_classStop", getGtmClassType(type));
2833
setOpenDialog(true);
2934
}
3035
};

src/app/(plain)/exercise/class/[id]/panel/WorkoutVideoPlayer.tsx

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@ import { axiosClient } from "@/apis/axiosClient";
1111
import { isAuthError } from "@/apis/errors";
1212
// import { useToastStore } from "@/states/useToastStore";
1313
import SenifitDialog from "@/components/SenifitDialog";
14+
import { getGtmClassType, pushGtmEvent } from "@/utils/gtm";
15+
import { WorkoutKind } from "@/types/IRoutine";
1416

1517
export interface IWorkoutVideo {
1618
id: number;
@@ -34,6 +36,7 @@ export interface IWorkoutVideoPlaylistProps {
3436
/** 인덱스 변경 콜백(옵션) */
3537
onIndexChange?: (index: number, video: IWorkoutVideo) => void;
3638
duration: number;
39+
type: "customized" | "popular" | ["thematic", WorkoutKind] | null;
3740
}
3841

3942
/** DOM 요소의 실시간 높이를 구하는 훅 */
@@ -68,6 +71,7 @@ export default function WorkoutVideoPlaylist({
6871
assetBaseUrl,
6972
onIndexChange,
7073
duration,
74+
type,
7175
}: IWorkoutVideoPlaylistProps) {
7276
const { isPhone } = useMedia();
7377
const router = useRouter();
@@ -160,6 +164,15 @@ export default function WorkoutVideoPlaylist({
160164
};
161165
}, [recordId]);
162166

167+
const progressFired = useRef(false);
168+
useEffect(() => {
169+
const halfDuration = Math.floor(duration * 30);
170+
if (!progressFired.current && seconds >= halfDuration && halfDuration > 0) {
171+
pushGtmEvent("click_Progress", getGtmClassType(type));
172+
progressFired.current = true;
173+
}
174+
}, [seconds, duration, type]);
175+
163176
// 이탈 방지 bypass 플래그 (앱 내부 이동 시 사용)
164177
const shouldBypassUnload = useRef(false);
165178

@@ -281,6 +294,7 @@ export default function WorkoutVideoPlaylist({
281294
isEnd={index === videos.length - 1}
282295
onEnd={notifyDone}
283296
seconds={seconds}
297+
type={type}
284298
/>
285299
</Box>
286300

src/app/(plain)/login/panel/LoginForm.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ import EyeOffIcon from "@/components/icons/EyeOffIcon";
2525
import InquiryButton from "@/components/InquiryButton";
2626
import BackgroundImage from "@/assets/images/login-background.png";
2727
import { signupGoogleForm } from "@/constants/signupGF";
28+
import { pushGtmEvent } from "@/utils/gtm";
2829

2930
type LoginFormValues = { id: string; password: string };
3031

@@ -57,6 +58,7 @@ export default function LoginForm() {
5758
const onSubmit = async (data: LoginFormValues) => {
5859
try {
5960
await login(data);
61+
pushGtmEvent("login_Success");
6062
setToastOpen({
6163
message: "로그인 성공! 오늘도 즐거운 시니핏 하세요!",
6264
});

src/app/(with-container)/(exercise)/exercise/check-selected/panel/Routine.tsx

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import { IRoutineDetail } from "@/types/IRoutineDetail";
1414
import { Button, Divider, Stack, Typography } from "@mui/material";
1515
import Link from "next/link";
1616
import React from "react";
17+
import { getGtmClassType, pushGtmEvent } from "@/utils/gtm";
1718

1819
const Routine = ({
1920
type,
@@ -116,7 +117,11 @@ const Routine = ({
116117
<Typography variant={"Heading1"}>{"이전"}</Typography>
117118
</Button>
118119
<Button
119-
onClick={() => setOpenModal(true)}
120+
id={"click_classStart"}
121+
onClick={() => {
122+
pushGtmEvent("click_classStart", getGtmClassType(type));
123+
setOpenModal(true);
124+
}}
120125
variant={"contained"}
121126
disableElevation
122127
sx={{

src/app/(with-container)/(exercise)/exercise/customized/page.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ import { IRoutineDetail } from "@/types/IRoutineDetail";
2323
import useProgramStore from "@/states/useProgramStore";
2424
import { useRouter } from "next/navigation";
2525
import SenifitDialog from "@/components/SenifitDialog";
26+
import { pushGtmEvent } from "@/utils/gtm";
2627

2728
const Page = () => {
2829
const [openDialog, setOpenDialog] = useState(false);
@@ -106,6 +107,7 @@ const Page = () => {
106107
<Stack
107108
component={"form"}
108109
onSubmit={handleSubmit(onSubmit, () => {
110+
pushGtmEvent("customized_optionError");
109111
setOpenDialog(true);
110112
})}
111113
direction={"column"}

src/app/(with-container)/(exercise)/exercise/done/[id]/page.tsx

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import useMedia from "@/hooks/useMedia";
1313
import Link from "next/link";
1414
import { isAuthError } from "@/apis/errors";
1515
import { useRouter } from "next/navigation";
16+
import { getGtmClassType, pushGtmEvent } from "@/utils/gtm";
1617

1718
const Page = () => {
1819
const { id } = useParams();
@@ -23,7 +24,7 @@ const Page = () => {
2324

2425
const searchParams = useSearchParams();
2526
const seconds = Number(searchParams.get("seconds")) || 0;
26-
const { selectedProgram } = useProgramStore();
27+
const { selectedProgram, type } = useProgramStore();
2728

2829
const { mutate } = useMutation({
2930
mutationFn: async () => {
@@ -38,7 +39,10 @@ const Page = () => {
3839

3940
useEffect(() => {
4041
mutate();
41-
}, [mutate]);
42+
if (selectedProgram && seconds >= selectedProgram.duration * 60) {
43+
pushGtmEvent("click_Finish", getGtmClassType(type));
44+
}
45+
}, [mutate, selectedProgram, seconds, type]);
4246

4347
return (
4448
<Stack
@@ -123,6 +127,7 @@ const Page = () => {
123127
gap={[1.25]}
124128
>
125129
<Button
130+
id={"click_RecordQuick"}
126131
component={Link}
127132
href={`/record/write/${id}`} // 기록 페이지로 이동
128133
variant={"contained"}

src/app/(with-container)/(exercise)/exercise/panel/VideoInfo.tsx

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import SenifitDialog from "@/components/SenifitDialog";
1414

1515
interface IVideoInfoProps extends IPopularRoutine {
1616
onButtonClick: () => void;
17+
gtmId?: string;
1718
}
1819

1920
const VideoInfo = ({
@@ -23,6 +24,7 @@ const VideoInfo = ({
2324
duration,
2425
description,
2526
onButtonClick,
27+
gtmId,
2628
}: IVideoInfoProps) => {
2729
const [openDialog, setOpenDialog] = useState(false);
2830

@@ -111,6 +113,7 @@ const VideoInfo = ({
111113
)}
112114

113115
<Button
116+
id={gtmId}
114117
onClick={() => setOpenDialog(true)}
115118
fullWidth
116119
disableElevation

0 commit comments

Comments
 (0)