Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 9 additions & 1 deletion web/src/app/(main)/home/page.tsx
Original file line number Diff line number Diff line change
@@ -1,16 +1,24 @@
"use client";

import Link from "next/link";
import { HonbobFirstVisitModal, HonbobLevelTestGuideModal } from "@/entities/honbob";
import { StoreList } from "@/entities/storeList/ui";
import { WelcomeUser } from "@/entities/user";
import { InputShapeCard, RECOMMEND_LIST } from "@/features/search";
import { GA4_RECOMMENDED_EVENTS, useGAClick } from "@/shared/lib";
import { TransitionLayout } from "@/shared/ui";

export default async function HomePage() {
const trackSearchButtonClick = useGAClick(GA4_RECOMMENDED_EVENTS.SELECT_CONTENT, {
select_content_type: "click_search_button",
page_path: "/home",
});

return (
<TransitionLayout>
<div className="flex flex-col bg-gray50 px-[20px] pb-[20px] rounded-b-[20px]">
<WelcomeUser />
<Link href={RECOMMEND_LIST.href}>
<Link href={RECOMMEND_LIST.href} onClick={() => trackSearchButtonClick()}>
<InputShapeCard />
</Link>
</div>
Expand Down
18 changes: 7 additions & 11 deletions web/src/app/(main)/map/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ import {
StoreBottomSheet,
} from "@/features/map/ui";
import { SearchBar } from "@/features/search";
import { CUSTOM_EVENTS, useGATimeSpent } from "@/shared/lib";
import { GA4_RECOMMENDED_EVENTS, useGAClick } from "@/shared/lib";
import { useFilterStore } from "@/shared/store";
import { TransitionLayout } from "@/shared/ui";

Expand All @@ -29,15 +29,6 @@ export default function MapPage() {
const { requestLocation } = useLocation(map);
const { isDragging, resetDragging } = useMapDrag(map);

// νŽ˜μ΄μ§€ 체λ₯˜ μ‹œκ°„ 좔적 (μ΅œμ†Œ 3초 이상 머물렀을 λ•Œλ§Œ 전솑)
useGATimeSpent(
CUSTOM_EVENTS.TIME_ON_PAGE,
{
page_name: "map",
},
3,
);

const { storeList, isFetching } = useStoreListData({ bounds, center });
const { isFilterOpen, filters, openFilter, closeFilter, setFilters } = useFilterStore();

Expand All @@ -52,9 +43,14 @@ export default function MapPage() {
resetDragging();
};

const trackSearchButtonClick = useGAClick(GA4_RECOMMENDED_EVENTS.SELECT_CONTENT, {
select_content_type: "click_search_button",
page_path: "/map",
});

return (
<TransitionLayout>
<Link href="/search">
<Link href="/search" onClick={() => trackSearchButtonClick()}>
<SearchBar />
</Link>
<MapView mapRef={mapContainerRef} initializeMap={initializeMap} />
Expand Down
14 changes: 13 additions & 1 deletion web/src/app/(stack)/honbob-level-info/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,20 @@

import { useRouter } from "next/navigation";
import { HonbobLevelCarousel } from "@/entities/honbob";
import { useUserState } from "@/entities/user";
import { GA4_RECOMMENDED_EVENTS, useGAClick } from "@/shared/lib";
import { TextButton, TransitionLayout } from "@/shared/ui";

export default function HonbobLevelInfoPage() {
const { userState } = useUserState();
const router = useRouter();

const trackMenuClick = useGAClick(GA4_RECOMMENDED_EVENTS.SELECT_CONTENT, {
select_content_type: "click_re_level_test",
at: "honbob_level_info_page",
user_level: userState.honbobLevel,
});

return (
<TransitionLayout>
<HonbobLevelCarousel />
Expand All @@ -16,7 +25,10 @@ export default function HonbobLevelInfoPage() {
isUnderline
color="gray"
isIcon={false}
onClick={() => router.push("/level-test")}
onClick={() => {
trackMenuClick();
router.push("/level-test");
}}
/>
</div>
</TransitionLayout>
Expand Down
23 changes: 14 additions & 9 deletions web/src/app/(stack)/level-test/[...id]/page.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
"use client";

import { useMemo } from "react";
import {
LevelTestDynamicPageContent,
LevelTestProgress,
Expand All @@ -14,16 +15,20 @@ function LevelTestContent() {
const { currentId, parsedStep, goToNext, goToPrevious, routeConstants } =
useLevelTestNavigation();

const showProgress = currentId && currentId !== routeConstants.RESULT;
const isResultPage = currentId === routeConstants.RESULT;

const eventParams = useMemo(
() => ({
page_name: "level_test_result_page",
result_level: result?.level,
is_result_page: isResultPage,
}),
[result, isResultPage],
);

// νŽ˜μ΄μ§€ 마운트 좔적 (currentIdκ°€ 변경될 λ•Œλ§ˆλ‹€ μƒˆλ‘œμš΄ 이벀트 전솑)
useGAMount(GA4_RECOMMENDED_EVENTS.PAGE_VIEW, {
page_name: "level_test_page",
page_id: currentId,
step: parsedStep?.toString() ?? "",
total_steps: totalSteps,
is_result_page: currentId === routeConstants.RESULT,
});
useGAMount(GA4_RECOMMENDED_EVENTS.PAGE_VIEW, eventParams, isResultPage);

const showProgress = currentId && currentId !== routeConstants.RESULT;

return (
<div className="flex h-full flex-col bg-gray0 px-[20px]">
Expand Down
79 changes: 75 additions & 4 deletions web/src/app/(stack)/store/[id]/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import {
import { ReviewSection } from "@/features/store/ui/ReviewSection";
import { queryKeys } from "@/shared/api";
import { useSticky } from "@/shared/hooks/useSticky";
import { CUSTOM_EVENTS, useGAInView } from "@/shared/lib";
import { TransitionLayout } from "@/shared/ui";

export default function StoreDetailPage(props: PageProps<"/store/[id]">) {
Expand Down Expand Up @@ -46,6 +47,52 @@ export default function StoreDetailPage(props: PageProps<"/store/[id]">) {
enabled: !!resolvedParams.id,
});

// GA μ„Ήμ…˜ 뷰포트 좔적 (50% 보일 λ•Œ, ν•œ 번만)

const menuTrackingRef = useGAInView<HTMLDivElement>({
eventName: CUSTOM_EVENTS.SECTION_VISIBLE,
params: {
section_name: "menu",
store_id: resolvedParams.id,
},
threshold: 0.5,
once: true,
enabled: !!store,
});

const seatTrackingRef = useGAInView<HTMLDivElement>({
eventName: CUSTOM_EVENTS.SECTION_VISIBLE,
params: {
section_name: "seat_info",
store_id: resolvedParams.id,
},
threshold: 0.5,
once: true,
enabled: !!store,
});

const reviewTrackingRef = useGAInView<HTMLDivElement>({
eventName: CUSTOM_EVENTS.SECTION_VISIBLE,
params: {
section_name: "review",
store_id: resolvedParams.id,
},
threshold: 0.5,
once: true,
enabled: !!store,
});

const similarTrackingRef = useGAInView<HTMLDivElement>({
eventName: CUSTOM_EVENTS.SECTION_VISIBLE,
params: {
section_name: "similar_store",
store_id: resolvedParams.id,
},
threshold: 0.5,
once: true,
enabled: !!store,
});

if (isLoading || !store) {
return (
<TransitionLayout>
Expand All @@ -68,10 +115,22 @@ export default function StoreDetailPage(props: PageProps<"/store/[id]">) {
>
<TabNav sectionRefs={sectionRefs} />
</div>
<div ref={menuRef} className="scroll-mt-[120px]">
<div
ref={(el) => {
menuRef.current = el;
menuTrackingRef.current = el;
}}
className="scroll-mt-[120px]"
>
<MenuSection menus={store.menus} handleSetZoomImageSrc={handleSetZoomImageSrc} />
</div>
<div ref={seatRef} className="scroll-mt-[120px]">
<div
ref={(el) => {
seatRef.current = el;
seatTrackingRef.current = el;
}}
className="scroll-mt-[120px]"
>
<SeatInfoSection seatInfo={store.seatInfo} />
<SeatImageGallery
storeName={store.name}
Expand All @@ -81,10 +140,22 @@ export default function StoreDetailPage(props: PageProps<"/store/[id]">) {
handleSetZoomImageSrc={handleSetZoomImageSrc}
/>
</div>
<div ref={reviewRef} className="scroll-mt-[120px]">
<div
ref={(el) => {
reviewRef.current = el;
reviewTrackingRef.current = el;
}}
className="scroll-mt-[120px]"
>
<ReviewSection storeId={store.storeId} memberId={userState?.memberId} />
</div>
<div ref={similarRef} className="scroll-mt-[120px]">
<div
ref={(el) => {
similarRef.current = el;
similarTrackingRef.current = el;
}}
className="scroll-mt-[120px]"
>
<SimilarStore storeId={store.storeId} />
</div>
<SuggestionCard storeId={store.storeId} />
Expand Down
17 changes: 4 additions & 13 deletions web/src/entities/honbob/ui/HonbobFirstVisitModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import Image from "next/image";
import { useRouter } from "next/navigation";
import { useState } from "react";
import { useUserState } from "@/entities/user";
import { CUSTOM_EVENTS, useGAClick, useSessionStorage } from "@/shared/lib";
import { GA4_RECOMMENDED_EVENTS, useGAClick, useSessionStorage } from "@/shared/lib";
import { BasicCharacter } from "@/shared/lib/assets";
import { Button, Icon, Modal, TextButton } from "@/shared/ui";

Expand All @@ -22,16 +22,7 @@ export function HonbobFirstVisitModal() {
!hasSeenModal && !userState.isLoggedIn && !userState.isLevelTestCompleted,
);

const trackStartButton = useGAClick(CUSTOM_EVENTS.BUTTON_CLICK, {
button_id: "honbob_level_test_start",
button_text: "μ‹œμž‘ν•˜κΈ°",
location: "first_visit_modal",
});

const trackCloseButton = useGAClick(CUSTOM_EVENTS.BUTTON_CLICK, {
button_id: "honbob_level_test_close",
location: "first_visit_modal",
});
const trackSelectContent = useGAClick(GA4_RECOMMENDED_EVENTS.SELECT_CONTENT);

const closeModal = () => {
setIsOpen(false);
Expand All @@ -56,7 +47,7 @@ export function HonbobFirstVisitModal() {
className="cursor-pointer self-end"
type="button"
onClick={() => {
trackCloseButton();
trackSelectContent({ select_content_type: "close_honbob_level_test" });
closeModal();
}}
>
Expand All @@ -77,7 +68,7 @@ export function HonbobFirstVisitModal() {
size="medium"
variant="primary"
onClick={() => {
trackStartButton();
trackSelectContent({ select_content_type: "start_honbob_level_test" });
router.push("/login");
}}
/>
Expand Down
14 changes: 13 additions & 1 deletion web/src/entities/store/ui/MenuItem.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { GA4_RECOMMENDED_EVENTS, useGAClick } from "@/shared/lib";
import { ImageWithFallback, Tag } from "@/shared/ui";
import type { Menu } from "../model/types";

Expand All @@ -8,6 +9,14 @@ export function MenuItem({
isRepresentative,
handleSetZoomImageSrc,
}: Menu & { handleSetZoomImageSrc: (src: string) => void }) {
const trackStoreMenuImageClick = useGAClick(GA4_RECOMMENDED_EVENTS.SELECT_CONTENT, {
select_content_type: "click_store_menu_image",
store_name: name,
menu_name: name,
menu_price: price.toLocaleString(),
menu_image_url: imageUrl,
});

return (
<article className="flex items-center gap-4">
<div className="h-[80px] w-[80px] bg-gray-100 rounded-[10px] relative">
Expand All @@ -18,7 +27,10 @@ export function MenuItem({
height={80}
fallbackIconSize={40}
className="h-[80px] w-[80px] rounded-[10px] object-cover cursor-pointer"
onClick={() => imageUrl && handleSetZoomImageSrc(imageUrl)}
onClick={() => {
trackStoreMenuImageClick();
imageUrl && handleSetZoomImageSrc(imageUrl);
}}
/>
</div>

Expand Down
17 changes: 15 additions & 2 deletions web/src/entities/storeList/ui/SimilarStoreCard.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { useRouter } from "next/navigation";
import type { SeatTypes, SimilarStoreRes } from "@/entities/storeList/api";
import { formatDistance } from "@/shared/lib";
import { formatDistance, GA4_RECOMMENDED_EVENTS, useGAClick } from "@/shared/lib";
import { ImageWithFallback, Tag } from "@/shared/ui";

export const SEAT_TYPES_MAP: Record<SeatTypes, string> = {
Expand Down Expand Up @@ -29,11 +29,24 @@ export function SimilarStoreCard({

const formattedDistance = formatDistance(distanceInMeters);

const trackSimilarStoreClick = useGAClick(GA4_RECOMMENDED_EVENTS.SELECT_CONTENT, {
select_content_type: "click_move_to_similar_store",
store_id: id,
store_name: name,
store_level: honbobLevel,
store_primary_category: primaryCategory,
store_distance_in_meters: distanceInMeters,
store_seat_types: parsedSeatTypes.join(","),
});

return (
<button
type="button"
className="flex flex-col gap-3"
onClick={() => router.push(`/store/${id}`)}
onClick={() => {
trackSimilarStoreClick();
router.push(`/store/${id}`);
}}
>
<div className="relative h-[156px] w-[156px] flex-shrink-0 bg-gray-100 rounded-[12px]">
<ImageWithFallback
Expand Down
11 changes: 9 additions & 2 deletions web/src/entities/storeList/ui/StoreList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import Link from "next/link";
import { getDefaultStationCenter, getLevelFilterDisplayValue } from "@/entities/storeList/lib";
import { useStoreListQuery } from "@/features/map/lib";
import { FilterBottomSheet, LevelFilterButton } from "@/features/map/ui";
import { useInfiniteScroll } from "@/shared/lib";
import { GA4_RECOMMENDED_EVENTS, useGAClick, useInfiniteScroll } from "@/shared/lib";
import { useFilterStore, useLocationStore } from "@/shared/store";
import { RoundedSelectBox } from "@/shared/ui";
import { StoreCard } from "./StoreCard";
Expand Down Expand Up @@ -44,6 +44,10 @@ export function StoreList() {
enabled: !isLoading && !error,
});

const trackStoreCardClick = useGAClick(GA4_RECOMMENDED_EVENTS.SELECT_CONTENT, {
select_content_type: "click_store_card",
});

return (
<div className="flex flex-col bg-gray0 pb-[20px]">
<FilterBottomSheet
Expand Down Expand Up @@ -78,7 +82,10 @@ export function StoreList() {
<ul className="flex flex-col gap-y-[15px] px-[20px]">
{storeList?.map((store) => (
<li key={store.id}>
<Link href={`/store/${store.id}`}>
<Link
href={`/store/${store.id}`}
onClick={() => trackStoreCardClick({ store_id: store.id, store_name: store.name })}
>
<StoreCard {...store} />
</Link>
</li>
Expand Down
Loading