Skip to content

Commit

Permalink
feat: 시작하기, 블록챗 믹스패널 연결 및 기타 버그 수정 (#41)
Browse files Browse the repository at this point in the history
* feat: nft, coin 페이지 로깅 추가

* fix: 최근 검색어 숨기기

* feat: 랜딩, 퀴즈 페이지 로깅 추가

* feat: 블록챗 로깅 추가

* fix: 500 에러 수정

* fix: 전체 질문 카테고리 정상 출력되도록 수정
  • Loading branch information
Najeong-Kim authored Feb 21, 2025
1 parent 4a32102 commit bc1fb66
Show file tree
Hide file tree
Showing 21 changed files with 294 additions and 34 deletions.
5 changes: 4 additions & 1 deletion src/app/chat/components/Chat/BotBubble.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { TextShimmer } from '@/components/ui/TextShimmer';
import ToolButton from './ToolButton';
import clsx from 'clsx';
import { useToast } from '@/contexts/ToastContext';
import useChatPageActions from '../../hooks/useChatPageActions';

const BotBubble = memo(
({
Expand All @@ -27,12 +28,14 @@ const BotBubble = memo(
const words = useMemo(() => text.split(' ') ?? [], [text]);
const actionRef = useRef<HTMLDivElement>(null);
const { showToast } = useToast();
const { handleCopyClick } = useChatPageActions();

const handleCopy = useCallback(() => {
const textToCopy = text.replace(/<[^>]*>/g, '');
navigator.clipboard.writeText(textToCopy);
showToast('답변이 복사되었어요!');
}, [text, showToast]);
handleCopyClick();
}, [text, showToast, handleCopyClick]);

useEffect(() => {
if (!isLastMessage) return;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import useRecommendQuestions from '../../hooks/useRecommendQuestions';
import Image from 'next/image';
import { Icon } from '@/assets/icons';
import useScreen from '@/hooks/useScreen';
import useChatPageActions from '../../hooks/useChatPageActions';

interface QuestionRecommendationProps {
handleSubmit: (text: string) => void;
Expand All @@ -15,6 +16,7 @@ interface QuestionRecommendationProps {
const QuestionRecommendation = ({ handleSubmit, handleViewChange }: QuestionRecommendationProps) => {
const { questions, selectedLevel, setSelectedLevel } = useRecommendQuestions();
const { isMobile } = useScreen();
const { handleRecommendationLevelClick, handleTotalQuestionClick } = useChatPageActions();

const handleClick = (text: string) => {
handleSubmit(text);
Expand All @@ -30,14 +32,20 @@ const QuestionRecommendation = ({ handleSubmit, handleViewChange }: QuestionReco
key={key}
level={value}
isSelected={selectedLevel === value}
onClick={() => setSelectedLevel(value)}
onClick={() => {
setSelectedLevel(value);
handleRecommendationLevelClick(value);
}}
/>
))}
</div>
<Questions questions={questions} handleClick={handleClick} />
<button
onClick={handleViewChange}
className="bg-system-light/60 flex items-center gap-[0.4rem] rounded-full border border-blue-100 py-[0.6rem] pl-[1.2rem] pr-[0.8rem] text-body-2-medium text-gray-700 hover:shadow-normal"
onClick={() => {
handleViewChange();
handleTotalQuestionClick();
}}
className="flex items-center gap-[0.4rem] rounded-full border border-blue-100 bg-system-light/60 py-[0.6rem] pl-[1.2rem] pr-[0.8rem] text-body-2-medium text-gray-700 hover:shadow-normal"
>
<span>질문 더보기</span>
<Icon name="ArrowRight" size={20} />
Expand Down
6 changes: 5 additions & 1 deletion src/app/chat/components/Questions/NavigationButton.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { Icon } from '@/assets/icons';
import clsx from 'clsx';
import { SwiperClass } from 'swiper/react';
import useChatPageActions from '../../hooks/useChatPageActions';

const NavigationButton = ({
direction,
Expand All @@ -11,6 +12,8 @@ const NavigationButton = ({
swiper: SwiperClass | null;
isSmall: boolean;
}) => {
const { handleRecommendationSwipe } = useChatPageActions();

return (
<div
className={clsx(
Expand All @@ -21,7 +24,7 @@ const NavigationButton = ({
<button
className={clsx(
'pointer-events-auto flex size-[2.8rem] items-center justify-center',
isSmall && 'bg-system-light rounded-full border border-blue-100',
isSmall && 'rounded-full border border-blue-100 bg-system-light',
direction === 'prev' && 'rotate-180'
)}
onClick={() => {
Expand All @@ -30,6 +33,7 @@ const NavigationButton = ({
} else {
swiper?.slideNext();
}
handleRecommendationSwipe();
}}
>
<Icon name="ArrowRight" className="text-gray-900" />
Expand Down
14 changes: 12 additions & 2 deletions src/app/chat/components/Questions/Questions.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import NavigationButton from './NavigationButton';
import { useEffect, useState } from 'react';
import 'swiper/css';
import clsx from 'clsx';
import useChatPageActions from '../../hooks/useChatPageActions';

interface QuestionsProps {
questions: RecommendQuestion[];
Expand All @@ -15,9 +16,12 @@ const SMALL_BREAKPOINT = 824;

const Questions = ({ questions, handleClick }: QuestionsProps) => {
const [swiper, setSwiper] = useState<SwiperClass | null>(null);
const [isSmall, setIsSmall] = useState(window.matchMedia(`(max-width: ${SMALL_BREAKPOINT}px)`).matches);
const [isSmall, setIsSmall] = useState(false);
const { handleRecommendationSwipe, handleRecommendationSelect } = useChatPageActions();

useEffect(() => {
setIsSmall(window.matchMedia(`(max-width: ${SMALL_BREAKPOINT}px)`).matches);

window.addEventListener('resize', () => {
setIsSmall(window.matchMedia(`(max-width: ${SMALL_BREAKPOINT}px)`).matches);
});
Expand Down Expand Up @@ -46,6 +50,9 @@ const Questions = ({ questions, handleClick }: QuestionsProps) => {
spaceBetween={12}
centeredSlides
loop
onSlideChangeTransitionEnd={() => {
handleRecommendationSwipe();
}}
onSwiper={(e) => {
setSwiper(e);
}}
Expand All @@ -57,7 +64,10 @@ const Questions = ({ questions, handleClick }: QuestionsProps) => {
{...question}
isActive={isActive}
isPrev={isPrev}
onClick={() => handleClick(question.question)}
onClick={() => {
handleClick(question.question);
handleRecommendationSelect(question.question);
}}
/>
)}
</SwiperSlide>
Expand Down
26 changes: 18 additions & 8 deletions src/app/chat/components/TotalQuestions/TotalQuestions.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { useEffect, useState } from 'react';
import { Level, LevelMap, QuestionCategory, QuestionCategoryMap, Questions } from '../../data';
import clsx from 'clsx';
import { fetchJson } from '@/utils/api';
import useChatPageActions from '../../hooks/useChatPageActions';

interface TotalQuestionsProps {
handleSubmit: (text: string) => void;
Expand All @@ -11,7 +12,7 @@ const TotalQuestions = ({ handleSubmit }: TotalQuestionsProps) => {
const [tab, setTab] = useState<Level>(Level.BEGINNER);
const [filter, setFilter] = useState<QuestionCategory>(QuestionCategoryMap[0]);
const [questions, setQuestions] = useState<Questions>([]);

const { handleTotalLevelClick, handleTotalCategoryClick, handleTotalQuestionSelect } = useChatPageActions();
useEffect(() => {
const fetchQuestions = async () => {
const questions = await fetchJson<Questions>('/api/info/level/questions');
Expand All @@ -35,7 +36,10 @@ const TotalQuestions = ({ handleSubmit }: TotalQuestionsProps) => {
'flex flex-1 items-start justify-center',
tab === value ? 'border-b-2 border-blue-400 text-blue-500' : 'border-b border-blue-100 text-gray-700'
)}
onClick={() => setTab(value)}
onClick={() => {
setTab(value);
handleTotalLevelClick(value);
}}
>
<h2 className="text-body-2-semibold">{LevelMap[value]}</h2>
</button>
Expand All @@ -49,23 +53,29 @@ const TotalQuestions = ({ handleSubmit }: TotalQuestionsProps) => {
'flex w-fit items-start justify-center rounded-[0.8rem] px-[1.6rem] py-[0.6rem]',
filter === value
? 'bg-system-dark text-body-2-semibold text-gray-100'
: 'bg-system-light border border-blue-100 px-[1.5rem] py-[0.5rem] text-body-2-medium text-gray-700'
: 'border border-blue-100 bg-system-light px-[1.5rem] py-[0.5rem] text-body-2-medium text-gray-700'
)}
onClick={() => setFilter(value)}
onClick={() => {
setFilter(value);
handleTotalCategoryClick(value);
}}
>
<h2 className="w-fit text-body-2-semibold">{value}</h2>
</button>
))}
</div>
<div className="scrollbar-hide -m-[0.6rem] grid h-full flex-1 grid-cols-1 overflow-y-auto pb-[15.6rem] mobile:pb-[10rem] tablet:grid-cols-2 desktop:grid-cols-3">
<div className="-m-[0.6rem] grid h-full flex-1 grid-cols-1 overflow-y-auto pb-[15.6rem] scrollbar-hide mobile:pb-[10rem] tablet:grid-cols-2 desktop:grid-cols-3">
{questions
.find((question) => question.level === tab)
?.questions.filter((question) => filter === '전체' || question.category_name === filter)
?.questions.filter((question) => filter === '전체' || question.category_name.trim() === filter)
.map((question) => (
<button
key={question.id}
className="hover:bg-gradient-card-2 dark:hover:bg-gradient-card-2-dark bg-system-light/60 m-[0.6rem] flex h-[13.1rem] flex-col gap-[0.9rem] rounded-[1.2rem] border border-blue-100 p-[1.6rem] text-start hover:shadow-normal"
onClick={() => handleClick(question.question)}
className="m-[0.6rem] flex h-[13.1rem] flex-col gap-[0.9rem] rounded-[1.2rem] border border-blue-100 bg-system-light/60 p-[1.6rem] text-start hover:bg-gradient-card-2 hover:shadow-normal dark:hover:bg-gradient-card-2-dark"
onClick={() => {
handleClick(question.question);
handleTotalQuestionSelect(question.question);
}}
>
<p className="text-body-3-semibold text-blue-500">{question.category_name}</p>
<p className="line-clamp-3 max-w-[18.8rem] text-body-1-medium">{question.question}</p>
Expand Down
85 changes: 85 additions & 0 deletions src/app/chat/hooks/useChatPageActions.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
import useLogging from '@/hooks/useLogging';
import { useCallback } from 'react';

const useChatPageActions = () => {
const { sendLog } = useLogging();

const handleRecommendationLevelClick = useCallback((level: string) => {
const eventProperties = {
레벨: level,
};

sendLog({ eventName: '채팅_질문_추천_레벨_클릭', eventProperties });
}, [sendLog]);

const handleRecommendationSwipe = useCallback(() => {
sendLog({ eventName: '채팅_질문_추천_스와이프' });
}, [sendLog]);

const handleRecommendationSelect = useCallback((question: string) => {
const eventProperties = {
질문: question,
};

sendLog({ eventName: '채팅_질문_추천_선택', eventProperties });
}, [sendLog]);

const handleTotalQuestionClick = useCallback(() => {
sendLog({ eventName: '채팅_질문_더보기_클릭' });
}, [sendLog]);

const handleTotalLevelClick = useCallback((level: string) => {
const eventProperties = {
레벨: level,
};

sendLog({ eventName: '채팅_전체_질문_레벨_클릭', eventProperties });
}, [sendLog]);

const handleTotalCategoryClick = useCallback((category: string) => {
const eventProperties = {
카테고리: category,
};

sendLog({ eventName: '채팅_전체_질문_카테고리_클릭', eventProperties });
}, [sendLog]);

const handleTotalQuestionSelect = useCallback((question: string) => {
const eventProperties = {
질문: question,
};

sendLog({ eventName: '채팅_전체_질문_선택', eventProperties });
}, [sendLog]);

const handleQuestionSubmit = useCallback((question: string) => {
const eventProperties = {
질문: question,
};

sendLog({ eventName: '채팅_질문_제출', eventProperties });
}, [sendLog]);

const handleCopyClick = useCallback(() => {
sendLog({ eventName: '채팅_질문_답변_복사' });
}, [sendLog]);

const handleRegenerateClick = useCallback(() => {
sendLog({ eventName: '채팅_질문_답변_재생성' });
}, [sendLog]);

return {
handleRecommendationLevelClick,
handleRecommendationSwipe,
handleRecommendationSelect,
handleTotalQuestionClick,
handleTotalLevelClick,
handleTotalCategoryClick,
handleTotalQuestionSelect,
handleQuestionSubmit,
handleCopyClick,
handleRegenerateClick,
};
};

export default useChatPageActions;
8 changes: 6 additions & 2 deletions src/app/chat/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,14 @@ import Chat from './components/Chat';
import { ChatType } from './data';
import useGenerateAnswer from './hooks/useGenerateAnswer';
import TotalQuestions from './components/TotalQuestions';
import useChatPageActions from './hooks/useChatPageActions';

export default function ChatPage() {
const [text, setText] = useState('');
const [chatList, setChatList] = useState<ChatType[]>([]);
const [view, setView] = useState<'recommendation' | 'total'>('recommendation');
const { generateAnswer, isLoading, isAnswering, handleFinishAnswering } = useGenerateAnswer();
const { handleQuestionSubmit, handleRegenerateClick } = useChatPageActions();

useEffect(() => {
if (chatList.length > 0) {
Expand All @@ -28,6 +30,7 @@ export default function ChatPage() {
const handleSubmit = useCallback(
async (text: string) => {
if (text.trim().length > 0) {
handleQuestionSubmit(text.trim());
setText('');
createChat(text.trim(), true);
const result = await generateAnswer(text);
Expand All @@ -36,7 +39,7 @@ export default function ChatPage() {
}
}
},
[createChat, generateAnswer]
[createChat, generateAnswer, handleQuestionSubmit]
);

const recreateChat = useCallback(
Expand All @@ -45,10 +48,11 @@ export default function ChatPage() {
const lastUserChat = [...chatList].find((chat) => chat.id === id);
if (lastUserChat) {
handleSubmit(lastUserChat.text);
handleRegenerateClick();
}
}
},
[chatList, handleSubmit]
[chatList, handleSubmit, handleRegenerateClick]
);

return (
Expand Down
21 changes: 21 additions & 0 deletions src/app/coin/hooks/useCoinPageActions.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import useLogging from '@/hooks/useLogging';
import { useCallback } from 'react';

const useCoinPageActions = () => {
const { sendLog } = useLogging();

const handleSaveImageClick = useCallback(() => {
sendLog({ eventName: '코인_이미지_저장' });
}, [sendLog]);

const handleOpenReceiptClick = useCallback(() => {
sendLog({ eventName: '코인_영수증_클릭' });
}, [sendLog]);

return {
handleSaveImageClick,
handleOpenReceiptClick,
};
};

export default useCoinPageActions;
Loading

0 comments on commit bc1fb66

Please sign in to comment.