Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
7 commits
Select commit Hold shift + click to select a range
523c362
fix:[#330] zod๋กœ ์ธํ•˜์—ฌ, ํ™ˆ์ด ๋‚˜์˜ค์ง€ ์•Š๋˜ ๋ถ€๋ถ„ ์ˆ˜์ •
cywin1018 Sep 7, 2025
e289db6
fix:[#330] a๋งํฌ๋ฅผ ์ด์šฉํ•˜์—ฌ url์„ ํ†ตํ•ด ์ด๋™์ด ๊ฐ€๋Šฅํ•˜๋„๋ก ์ˆ˜์ •
cywin1018 Sep 7, 2025
06bca4e
feat: [#330]ํ™ˆ ๋ฆฌํฌ๋ฃจํŠธ ์นด๋“œ ํด๋ฆญ ์‹œ ์ƒ์„ธ ๋ชจ๋‹ฌ ์ถ”๊ฐ€ ๋ฐ ๋ฐ์ดํ„ฐ ๋ณ€ํ™˜ ๋กœ์ง ๊ตฌํ˜„
cywin1018 Sep 7, 2025
6a0aea2
fix: [#330] ์ฒดํฌ๋ฆฌ์ŠคํŠธ์—์„œ AddIcon ์ œ๊ฑฐ ๋ฐ ์ปค๋ฎค๋‹ˆํ‹ฐ ์™ผ์ชฝ ์‚ฌ์ด๋“œ๋ฐ”์—์„œ ๊ธฐ๋ณธ ํ”„๋กœํ•„ ์ด๋ฏธ์ง€ ์ถ”๊ฐ€, ํ™ˆ ๋“œ๋ฆฌ๋จธ์—โ€ฆ
cywin1018 Sep 7, 2025
7453db5
feat: [#330] ํ• ์ผ ์ถ”๊ฐ€ ๋ฐ ์ทจ์†Œ ๊ธฐ๋Šฅ ๊ตฌํ˜„, ํ† ์ŠคํŠธ ๋ฉ”์‹œ์ง€ ์ถ”๊ฐ€
cywin1018 Sep 7, 2025
516a051
feat: [#330] HotPopularItem ์ธํ„ฐํŽ˜์ด์Šค์— todoGroupId ์ถ”๊ฐ€ ๋ฐ ๋ถˆํ•„์š”ํ•œ console.log ์ œ๊ฑฐ
cywin1018 Sep 7, 2025
a5186aa
feat: [#330] ํ• ์ผ ์ถ”๊ฐ€ ์‹œ Amplitude ์ด๋ฒคํŠธ ํŠธ๋ž˜ํ‚น ๊ธฐ๋Šฅ ์ถ”๊ฐ€ ๋ฐ ๊ด€๋ จ ์ฝ”๋“œ ์ˆ˜์ •
cywin1018 Sep 7, 2025
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
7 changes: 1 addition & 6 deletions src/common/CheckList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ import { useDeleteTodoMutation } from '@hook/todo/useDeleteTodoMutation';
import { ReactTagManager } from 'react-gtm-ts';
import { useAddTodoMutation } from '@hook/todo/useAddTodoMutation.ts';
import Plus from '@assets/icons/plus.svg?react';
import AddIcon from '@assets/icons/AddIcon.svg?react';
import BookMarkIcon from '@assets/icons/bookmark.svg?react';
import { useUpdateTodoMutation } from '@hook/todo/useUpdateTodoMutation.ts';

Expand Down Expand Up @@ -84,7 +83,7 @@ const CheckList = ({
});
console.log('Amplitude event sent: todo_create_attempt (inpage)');
}

mutate(
{ todoTitle: trimmedText },
{
Expand Down Expand Up @@ -263,10 +262,6 @@ const CheckList = ({
</>
) : (
<>
<div className="mr-3 flex items-center gap-1 text-gray-500">
<AddIcon className="h-[18px] w-[18px]" />
<span className="text-sm font-B03-SB">3</span>
</div>
<div className="mr-3 flex items-center gap-1 text-gray-500">
<BookMarkIcon className="h-[18px] w-[18px]" />
<span className="text-sm font-B03-SB">999</span>
Expand Down
2 changes: 1 addition & 1 deletion src/hook/community/query/useGetHotPopularQuery.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ export interface HotPopularItem {
description: string;
saveCount: number;
isSaved: boolean;
todoGroupId: number;
}

interface HotPopularApiResponse {
Expand All @@ -29,7 +30,6 @@ export const useGetHotPopularQuery = () => {
const res = await api.get('/v1/community/todos/popular', {
params: { jobName: selectedJobName },
});
console.log(res.data);
const body = res.data as HotPopularApiResponse;
return Array.isArray(body?.data) ? body.data : [];
},
Expand Down
1 change: 1 addition & 0 deletions src/hook/useHomeQuery.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ interface RecruitProps {
active: number;
jobName: string;
postDate: string;
url: string;
}
const NewRecruit = async (pageNum: number, postDate: string) => {
const token = localStorage.getItem('accessToken');
Expand Down
6 changes: 4 additions & 2 deletions src/pages/community/components/CommunityContents.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { useCommunityAddTodoMutation } from '@hook/community/useCommunityAddTodo
import { useDeleteCommunityTodosMutation } from '@hook/community/useDeleteCommunityTodos';
import ToastModal from '@common/modal/ToastModal';
import Info from '@assets/icons/info.svg?react';
import { trackTodoImport } from '@utils/amplitude';

type CommunityItem = {
id: number;
Expand Down Expand Up @@ -48,7 +49,7 @@ const CommunityContents = ({
return base;
}, [filtered, sort]);

const toggleAdd = (id: number, isAdded: boolean) => {
const toggleAdd = (id: number, isAdded: boolean, todoTitle: string) => {
if (isAdded) {
deleteTodoMutation.mutate(
{ id },
Expand All @@ -65,6 +66,7 @@ const CommunityContents = ({
}
);
} else {
trackTodoImport(todoTitle); // Amplitude ์ด๋ฒคํŠธ ํŠธ๋ž˜ํ‚น
addTodoMutation.mutate(
{ id },
{
Expand Down Expand Up @@ -131,7 +133,7 @@ const CommunityContents = ({

<button
type="button"
onClick={() => toggleAdd(post.id, isAdded)}
onClick={() => toggleAdd(post.id, isAdded, post.description)}
className={
isAdded
? 'p-2 text-purple-500 font-B03-SB'
Expand Down
11 changes: 6 additions & 5 deletions src/pages/community/components/CommunityLeftSide.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { useGetHotPopularQuery } from '@hook/community/query/useGetHotPopularQue
import Bookmark from '@assets/icons/bookmark.svg?react';
import Arrow from '@assets/icons/arrow.svg?react';
import jobNames, { findJobIdByName } from '@utils/data/community/jobs';
import BaseImage from '@assets/images/profile.png';

const CommunityLeftSide = () => {
const navigate = useNavigate();
Expand All @@ -27,7 +28,6 @@ const CommunityLeftSide = () => {
<div
className="mt-[30px] flex w-full cursor-pointer flex-row items-center justify-end text-gray-500 font-B02-SB"
onClick={() => {
// ํ•˜๋“œ์ฝ”๋”ฉ ๋งคํ•‘๋œ jobId ์‚ฌ์šฉ
const id = findJobIdByName(selectedJobName);
navigate(`/others/${id ?? 1}`);
}}
Expand All @@ -46,23 +46,24 @@ const CommunityLeftSide = () => {
{popularTodos.map((item, idx) => (
<div
key={item.id}
className="flex w-full flex-row items-start justify-between py-4"
className="flex w-full cursor-pointer flex-row items-start justify-between py-4"
onClick={() => navigate(`/otherslist/${item.id}`)}
>
<div className="flex flex-row items-center gap-[15px]">
<div className="text-purple-500 font-T05-SB">{idx + 1}</div>
<div className="flex flex-row items-center gap-[15px]">
<img
src={item.imageUrl}
src={item.imageUrl || BaseImage}
alt="ํ”„๋กœํ•„์ด๋ฏธ์ง€"
className="h-[30px] w-[30px] rounded-full bg-gray-50"
/>

<div className="flex flex-col">
<div className="flex w-full flex-row items-start justify-between gap-[10px]">
<div className="flex-1 break-words text-black font-B01-SB">
<div className="w-[170px] min-w-0 flex-1 truncate text-black font-B01-SB">
{item.description}
</div>
<div className="mr-2 shrink-0 whitespace-nowrap text-gray-500 font-C01-R">
<div className="shrink-0 whitespace-nowrap text-gray-500 font-C01-R">
{item.dDay}
</div>
</div>
Expand Down
2 changes: 1 addition & 1 deletion src/pages/home/components/HomeDreamer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ const HomeDreamer = () => {
<div className="text-gray-900 font-T02-B">{title}</div>
<div
className="flex cursor-pointer flex-row items-center text-gray-500 font-B02-SB"
onClick={() => navigate('/jobfound')}
onClick={() => navigate('/community')}
>
๋” ๋ณด๋Ÿฌ๊ฐ€๊ธฐ
<Arrow />
Expand Down
78 changes: 77 additions & 1 deletion src/pages/home/components/HomeRecruit.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,17 +8,77 @@ import { useRecruitQuery } from '@hook/useHomeQuery';
import LoadingSpinner from '@common/LoadingSpinner';
import { useFilterStore } from '@store/filterStore';
import { useNavigate } from 'react-router-dom';
import CardDetail from '@pages/jobSearch/components/CardDetail';
import { RecruitItem } from '@validation/recruit/recruitSchema';

interface RecruitData {
id: number;
title: string;
companyName: string;
jobName: string;
postDate: string;
count: number | string;
url: string;
active: number;
locationName?: string | null;
jobTypeName?: string;
experienceLevel?: string;
requiredEducationLevel?: string;
closeType?: string;
salary?: string;
postTimestamp?: string;
'expiration-timestamp'?: string;
'expiration-date'?: string;
deadline?: string;
}

const HomeRecruit = () => {
const regionName = useUserStore((state) => state.regionName);
const setSelection = useFilterStore((state) => state.setSelection);
const [likedItems, setLikedItems] = useState<{ [key: number]: boolean }>({});
const [selectedCard, setSelectedCard] = useState<RecruitItem | null>(null);
const [isModalOpen, setIsModalOpen] = useState(false);

const toggleLike = (id: number) => {
setLikedItems((prev) => ({
...prev,
[id]: !prev[id],
}));
};

const convertToRecruitItem = (data: RecruitData): RecruitItem => {
return {
url: data.url || '',
active: data.active || 1,
title: data.title || '',
jobName: data.jobName || '',
companyName: data.companyName || '',
locationName: data.locationName || null,
jobTypeName: data.jobTypeName || '',
experienceLevel: data.experienceLevel || '',
requiredEducationLevel: data.requiredEducationLevel || '',
closeType: data.closeType || '',
salary: data.salary || '',
id: String(data.id),
postTimestamp: data.postTimestamp || '',
postDate: data.postDate || '',
'expiration-timestamp': data['expiration-timestamp'] || '',
'expiration-date': data['expiration-date'] || '',
deadline: data.deadline || '',
count:
typeof data.count === 'string' ? parseInt(data.count) : data.count || 0,
};
};
Comment on lines +49 to +71
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

โš ๏ธ Potential issue

ํ™œ์„ฑ ์ƒํƒœ(active) 0์ด 1๋กœ ์˜ค์ธ๋  ์ˆ˜ ์žˆ์Œ + ์ •์ˆ˜ ํŒŒ์‹ฑ radix ๋ช…์‹œ

  • active: data.active || 1์€ 0(๋น„ํ™œ์„ฑ)์„ 1๋กœ ๋ฎ์–ด์จ ๋ฒ„๋ฆฝ๋‹ˆ๋‹ค. nullish ๋ณ‘ํ•ฉ(??)์„ ์“ฐ์„ธ์š”.
  • count: parseInt ์‚ฌ์šฉ ์‹œ 10์ง„์ˆ˜ radix๋ฅผ ๋ช…์‹œํ•˜์„ธ์š”.

์ ์šฉ ์ œ์•ˆ:

   const convertToRecruitItem = (data: RecruitData): RecruitItem => {
     return {
       url: data.url || '',
-      active: data.active || 1,
+      active: data.active ?? 1,
       title: data.title || '',
       jobName: data.jobName || '',
       companyName: data.companyName || '',
       locationName: data.locationName || null,
       jobTypeName: data.jobTypeName || '',
       experienceLevel: data.experienceLevel || '',
       requiredEducationLevel: data.requiredEducationLevel || '',
       closeType: data.closeType || '',
       salary: data.salary || '',
       id: String(data.id),
       postTimestamp: data.postTimestamp || '',
       postDate: data.postDate || '',
       'expiration-timestamp': data['expiration-timestamp'] || '',
       'expiration-date': data['expiration-date'] || '',
       deadline: data.deadline || '',
       count:
-        typeof data.count === 'string' ? parseInt(data.count) : data.count || 0,
+        typeof data.count === 'string'
+          ? parseInt(data.count, 10)
+          : (data.count ?? 0),
     };
   };
๐Ÿ“ Committable suggestion

โ€ผ๏ธ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const convertToRecruitItem = (data: RecruitData): RecruitItem => {
return {
url: data.url || '',
active: data.active || 1,
title: data.title || '',
jobName: data.jobName || '',
companyName: data.companyName || '',
locationName: data.locationName || null,
jobTypeName: data.jobTypeName || '',
experienceLevel: data.experienceLevel || '',
requiredEducationLevel: data.requiredEducationLevel || '',
closeType: data.closeType || '',
salary: data.salary || '',
id: String(data.id),
postTimestamp: data.postTimestamp || '',
postDate: data.postDate || '',
'expiration-timestamp': data['expiration-timestamp'] || '',
'expiration-date': data['expiration-date'] || '',
deadline: data.deadline || '',
count:
typeof data.count === 'string' ? parseInt(data.count) : data.count || 0,
};
};
const convertToRecruitItem = (data: RecruitData): RecruitItem => {
return {
url: data.url || '',
active: data.active ?? 1,
title: data.title || '',
jobName: data.jobName || '',
companyName: data.companyName || '',
locationName: data.locationName || null,
jobTypeName: data.jobTypeName || '',
experienceLevel: data.experienceLevel || '',
requiredEducationLevel: data.requiredEducationLevel || '',
closeType: data.closeType || '',
salary: data.salary || '',
id: String(data.id),
postTimestamp: data.postTimestamp || '',
postDate: data.postDate || '',
'expiration-timestamp': data['expiration-timestamp'] || '',
'expiration-date': data['expiration-date'] || '',
deadline: data.deadline || '',
count:
typeof data.count === 'string'
? parseInt(data.count, 10)
: (data.count ?? 0),
};
};
๐Ÿค– Prompt for AI Agents
In src/pages/home/components/HomeRecruit.tsx around lines 49 to 71, the
converter incorrectly treats active: data.active || 1 which turns a legitimate 0
into 1 and parseInt lacks a radix; change to use nullish coalescing for active
(active: data.active ?? 1) so only null/undefined default to 1, and when parsing
count explicitly pass a radix 10 (e.g. parseInt(data.count, 10)) while
preserving the existing fallback to 0 when count is missing or not a number.


const handleCardClick = (data: RecruitData) => {
console.log('Card clicked:', data);
const recruitItem = convertToRecruitItem(data);
console.log('Converted item:', recruitItem);
setSelectedCard(recruitItem);
setIsModalOpen(true);
console.log('Modal should open');
};

const isLoggedIn = localStorage.getItem('accessToken');
const navigate = useNavigate();

Expand Down Expand Up @@ -63,10 +123,14 @@ const HomeRecruit = () => {
<div
key={data.id}
className="flex min-h-[312px] w-[384px] cursor-pointer flex-col items-start rounded-[30px] border-[1.2px] border-gray-300 p-[30px] hover:shadow-shadow2"
onClick={() => handleCardClick(data)}
>
<div
className="flex w-full flex-col items-end"
onClick={() => toggleLike(data.id)}
onClick={(e) => {
e.stopPropagation();
toggleLike(data.id);
}}
>
{likedItems[data.id] ? <FullLike /> : <Like />}
</div>
Expand All @@ -92,6 +156,18 @@ const HomeRecruit = () => {
</div>
))}
</div>

{isModalOpen && selectedCard && (
<div className="fixed inset-0 z-50 flex items-center justify-center bg-black bg-opacity-50">
<CardDetail
item={selectedCard}
onClose={() => {
console.log('Closing modal');
setIsModalOpen(false);
}}
/>
</div>
)}
</div>
);
};
Expand Down
6 changes: 4 additions & 2 deletions src/pages/jobDetail/components/TabContent/SproutContent.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { useState } from 'react';
import ToastModal from '@common/modal/ToastModal';
import Info from '@assets/icons/info.svg?react';
import { useAddMyTodoMutation } from '@hook/jobinfo/useAddMyTodoMutation.ts';
import { trackTodoImport } from '@utils/amplitude';

interface SproutContentProps {
jobId: number;
Expand All @@ -17,10 +18,11 @@ const SproutContent = ({ data }: SproutContentProps) => {
const [completedId, setCompletedId] = useState<Set<number>>(new Set());
const [showToast, setShowToast] = useState(false);

const handleAdd = (jobTodoId: number) => {
const handleAdd = (jobTodoId: number, title: string) => {
if (completedId.has(jobTodoId)) return;

setClickedId(jobTodoId);
trackTodoImport(title); // Amplitude ์ด๋ฒคํŠธ ํŠธ๋ž˜ํ‚น
mutate(jobTodoId, {
onSuccess: () => {
setCompletedId((prev) => new Set(prev).add(jobTodoId));
Expand Down Expand Up @@ -63,7 +65,7 @@ const SproutContent = ({ data }: SproutContentProps) => {
: 'bg-purple-500 text-purple-100 hover:bg-purple-600'
}`}
disabled={isLoading || isCompleted}
onClick={() => handleAdd(sprout.JobTodoId)}
onClick={() => handleAdd(sprout.JobTodoId, sprout.title)}
>
{isCompleted
? 'ํ• ์ผ ์ถ”๊ฐ€ ์™„๋ฃŒ'
Expand Down
6 changes: 4 additions & 2 deletions src/pages/jobDetail/components/TabContent/TreeContents.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { useState } from 'react';
import ToastModal from '@common/modal/ToastModal';
import Info from '@assets/icons/info.svg?react';
import { useAddMyTodoMutation } from '@hook/jobinfo/useAddMyTodoMutation.ts';
import { trackTodoImport } from '@utils/amplitude';

interface TreeContentsProps {
jobId: number;
Expand All @@ -17,10 +18,11 @@ const TreeContents = ({ data }: TreeContentsProps) => {
const [completedId, setCompletedId] = useState<Set<number>>(new Set());
const [showToast, setShowToast] = useState(false);

const handleAdd = (jobTodoId: number) => {
const handleAdd = (jobTodoId: number, title: string) => {
if (completedId.has(jobTodoId)) return;

setClickedId(jobTodoId);
trackTodoImport(title); // Amplitude ์ด๋ฒคํŠธ ํŠธ๋ž˜ํ‚น
mutate(jobTodoId, {
onSuccess: () => {
setCompletedId((prev) => new Set(prev).add(jobTodoId));
Expand Down Expand Up @@ -63,7 +65,7 @@ const TreeContents = ({ data }: TreeContentsProps) => {
: 'bg-purple-500 text-purple-100 hover:bg-purple-600'
}`}
disabled={isLoading || isCompleted}
onClick={() => handleAdd(tree.JobTodoId)}
onClick={() => handleAdd(tree.JobTodoId, tree.title)}
>
{isCompleted
? 'ํ• ์ผ ์ถ”๊ฐ€ ์™„๋ฃŒ'
Expand Down
Loading