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
50 changes: 50 additions & 0 deletions apps/web/src/features/cart/CartPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import {
removeCartItem,
updateCartItemQuantity,
} from "../../services/cartApi";
import { recordUserBehaviorEvent } from "../../services/eventLogApi";
import {
clearStoredCartId,
clearStoredPendingOrder,
Expand Down Expand Up @@ -123,6 +124,20 @@ export function CartPage() {
loadCart();
}, [loadCart]);

useEffect(() => {
void recordUserBehaviorEvent({
event_name: "cart_viewed",
user_id: userId,
session_id: null,
entity_type: storedCartId ? "cart" : null,
entity_id: storedCartId,
properties: {
page_path: window.location.pathname,
cart_id: storedCartId,
},
});
}, [storedCartId, userId]);

useEffect(() => {
const nextDrafts = cartItems.reduce<Record<string, string>>((drafts, item) => {
drafts[item.cart_item_id] = String(item.quantity);
Expand All @@ -137,6 +152,24 @@ export function CartPage() {
return;
}

const targetItem = cartItems.find((item) => item.cart_item_id === cartItemId);

void recordUserBehaviorEvent({
event_name: "cart_item_remove_clicked",
user_id: userId,
session_id: null,
entity_type: "cart",
entity_id: storedCartId,
properties: {
page_path: window.location.pathname,
cart_id: storedCartId,
cart_item_id: cartItemId,
product_id: targetItem?.product_id ?? null,
product_name: targetItem?.product_name ?? null,
quantity: targetItem?.quantity ?? null,
},
});

try {
setRemovingItemId(cartItemId);
setActionMessage(null);
Expand All @@ -161,6 +194,23 @@ export function CartPage() {

const normalizedQuantity = Math.max(0, Math.min(99, nextQuantity));

void recordUserBehaviorEvent({
event_name: "cart_quantity_change_clicked",
user_id: userId,
session_id: null,
entity_type: "cart",
entity_id: storedCartId,
properties: {
page_path: window.location.pathname,
cart_id: storedCartId,
cart_item_id: item.cart_item_id,
product_id: item.product_id,
product_name: item.product_name ?? null,
previous_quantity: item.quantity,
next_quantity: normalizedQuantity,
},
});

try {
setUpdatingItemId(item.cart_item_id);
setActionMessage(null);
Expand Down
51 changes: 51 additions & 0 deletions apps/web/src/features/checkout/CheckoutPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import {
enterCheckout,
simulatePayment,
} from "../../services/checkoutApi";
import { recordUserBehaviorEvent } from "../../services/eventLogApi";
import {
clearStoredCartId,
clearStoredPendingOrder,
Expand Down Expand Up @@ -169,6 +170,20 @@ export function CheckoutPage() {
loadCheckout();
}, [checkoutCartId, userId]);

useEffect(() => {
void recordUserBehaviorEvent({
event_name: "checkout_started",
user_id: userId,
session_id: null,
entity_type: checkoutCartId ? "cart" : null,
entity_id: checkoutCartId,
properties: {
page_path: window.location.pathname,
cart_id: checkoutCartId,
},
});
}, [checkoutCartId, userId]);

const handleCouponCodeChange = (event: ChangeEvent<HTMLInputElement>) => {
setCouponCode(event.target.value);
};
Expand Down Expand Up @@ -225,6 +240,24 @@ export function CheckoutPage() {
return;
}

void recordUserBehaviorEvent({
event_name: "order_create_clicked",
user_id: userId,
session_id: null,
entity_type: "cart",
entity_id: checkoutCartId,
properties: {
page_path: window.location.pathname,
cart_id: checkoutCartId,
coupon_name:
appliedCoupon?.coupon?.coupon_name ??
appliedCoupon?.coupon_name ??
appliedCoupon?.coupon_code ??
null,
final_amount: finalAmount,
},
});

try {
setIsCreatingOrder(true);
setErrorMessage(null);
Expand Down Expand Up @@ -273,6 +306,24 @@ export function CheckoutPage() {
return;
}

void recordUserBehaviorEvent({
event_name:
simulateResult === "success"
? "payment_success_clicked"
: "payment_fail_clicked",
user_id: userId,
session_id: null,
entity_type: "order",
entity_id: createdOrder.order_id,
properties: {
page_path: window.location.pathname,
cart_id: checkoutCartId,
order_id: createdOrder.order_id,
payment_method: PAYMENT_METHOD,
simulate_result: simulateResult,
},
});

try {
setIsPaying(true);
setErrorMessage(null);
Expand Down
14 changes: 14 additions & 0 deletions apps/web/src/features/coupons/CouponWalletPage.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { useEffect, useState } from "react";
import { Link } from "react-router-dom";
import { getUserCoupons } from "../../services/couponApi";
import { recordUserBehaviorEvent } from "../../services/eventLogApi";
import { getStoredUser } from "../../stores/userStore";
import type { UsedCoupon, UserCoupon, UserCouponWalletResponse } from "../../types/coupon";

Expand Down Expand Up @@ -156,6 +157,19 @@ export function CouponWalletPage() {
loadCoupons();
}, [userId]);

useEffect(() => {
void recordUserBehaviorEvent({
event_name: "coupon_wallet_viewed",
user_id: userId,
session_id: null,
entity_type: null,
entity_id: null,
properties: {
page_path: window.location.pathname,
},
});
}, [userId]);

if (!userId) {
return (
<section className="coupon-wallet-page">
Expand Down
14 changes: 14 additions & 0 deletions apps/web/src/features/orders/OrderHistoryPage.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { useEffect, useMemo, useState } from "react";
import { Link } from "react-router-dom";
import { recordUserBehaviorEvent } from "../../services/eventLogApi";
import { getOrderHistory } from "../../services/orderApi";
import { getStoredUser } from "../../stores/userStore";
import type { OrderHistoryItem, OrderItem } from "../../types/order";
Expand Down Expand Up @@ -296,6 +297,19 @@ export function OrderHistoryPage() {
loadOrders();
}, [userId]);

useEffect(() => {
void recordUserBehaviorEvent({
event_name: "order_history_viewed",
user_id: userId,
session_id: null,
entity_type: null,
entity_id: null,
properties: {
page_path: window.location.pathname,
},
});
}, [userId]);

if (!userId) {
return (
<section className="order-history-page">
Expand Down
36 changes: 36 additions & 0 deletions apps/web/src/features/products/ProductDetailPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { useEffect, useState } from "react";
import { Link, useLocation, useParams } from "react-router-dom";
import { addCartItem, createCart } from "../../services/cartApi";
import { getProductDetail } from "../../services/catalogApi";
import { recordUserBehaviorEvent } from "../../services/eventLogApi";
import { getProductReviews } from "../../services/reviewApi";
import {
clearStoredCartId,
Expand Down Expand Up @@ -138,6 +139,26 @@ export function ProductDetailPage() {
loadProductDetail();
}, [productId]);

useEffect(() => {
if (!product) {
return;
}

void recordUserBehaviorEvent({
event_name: "product_detail_viewed",
user_id: user?.user_id ?? null,
session_id: null,
entity_type: "product",
entity_id: product.product_id,
properties: {
page_path: window.location.pathname,
product_id: product.product_id,
product_name: product.product_name,
source_page: "product_detail",
},
});
}, [product?.product_id, user?.user_id]);

useEffect(() => {
async function loadProductReviews() {
if (!productId) {
Expand Down Expand Up @@ -210,6 +231,21 @@ export function ProductDetailPage() {
return;
}

void recordUserBehaviorEvent({
event_name: "product_add_to_cart_clicked",
user_id: user?.user_id ?? null,
session_id: null,
entity_type: "product",
entity_id: product.product_id,
properties: {
page_path: window.location.pathname,
product_id: product.product_id,
product_name: product.product_name,
source_page: "product_detail",
quantity,
},
});

if (!user) {
setCartMessage(null);
setCartErrorMessage("로그인 후 장바구니에 상품을 담을 수 있습니다.");
Expand Down
31 changes: 31 additions & 0 deletions apps/web/src/features/products/ProductListPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import {
import { Link } from "react-router-dom";
import { addCartItem, createCart } from "../../services/cartApi";
import { getCategories, getProducts } from "../../services/catalogApi";
import { recordUserBehaviorEvent } from "../../services/eventLogApi";
import {
clearStoredCartId,
clearStoredPendingOrder,
Expand Down Expand Up @@ -58,6 +59,21 @@ export function ProductListPage() {

const productListTopRef = useRef<HTMLDivElement | null>(null);

useEffect(() => {
const user = getStoredUser();

void recordUserBehaviorEvent({
event_name: "product_list_viewed",
user_id: user?.user_id ?? null,
session_id: null,
entity_type: null,
entity_id: null,
properties: {
page_path: window.location.pathname,
},
});
}, []);

const scrollToProductListTop = useCallback(() => {
requestAnimationFrame(() => {
productListTopRef.current?.scrollIntoView({
Expand Down Expand Up @@ -152,6 +168,21 @@ export function ProductListPage() {

const user = getStoredUser();

void recordUserBehaviorEvent({
event_name: "product_add_to_cart_clicked",
user_id: user?.user_id ?? null,
session_id: null,
entity_type: "product",
entity_id: product.product_id,
properties: {
page_path: window.location.pathname,
product_id: product.product_id,
product_name: product.product_name,
source_page: "product_list",
quantity: QUICK_ADD_QUANTITY,
},
});

if (!user) {
setCartFeedbackMessage(null);
setCartErrorMessage("로그인 후 장바구니에 상품을 담을 수 있습니다.");
Expand Down
37 changes: 36 additions & 1 deletion apps/web/src/features/reviews/ReviewCreatePage.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { FormEvent, useState } from "react";
import { FormEvent, useEffect, useState } from "react";
import { Link, useNavigate, useSearchParams } from "react-router-dom";
import { ApiError } from "../../services/apiClient";
import { recordUserBehaviorEvent } from "../../services/eventLogApi";
import { createReview } from "../../services/reviewApi";
import { getStoredUser } from "../../stores/userStore";

Expand Down Expand Up @@ -42,9 +43,43 @@ export function ReviewCreatePage() {

const isInvalidReviewTarget = !validProductId || !validOrderItemId;

useEffect(() => {
void recordUserBehaviorEvent({
event_name: "review_create_page_viewed",
user_id: userId,
session_id: null,
entity_type: validProductId ? "product" : null,
entity_id: validProductId,
properties: {
page_path: window.location.pathname,
product_id: validProductId,
order_item_id: validOrderItemId,
product_name: productName,
},
});
}, [userId, validProductId, validOrderItemId, productName]);

const handleSubmit = async (event: FormEvent<HTMLFormElement>) => {
event.preventDefault();

void recordUserBehaviorEvent({
event_name: "review_submit_clicked",
user_id: userId,
session_id: null,
entity_type: validProductId ? "product" : null,
entity_id: validProductId,
properties: {
page_path: window.location.pathname,
product_id: validProductId,
order_item_id: validOrderItemId,
product_name: productName,
rating,
recommendation,
has_review_title: Boolean(reviewTitle.trim()),
has_review_content: Boolean(reviewContent.trim()),
},
});

setErrorMessage(null);
setSuccessMessage(null);

Expand Down
Loading
Loading