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
83 changes: 57 additions & 26 deletions apps/client/src/pages/jobPins/JobPins.tsx
Original file line number Diff line number Diff line change
@@ -1,42 +1,73 @@
import { Card } from '@pinback/design-system/ui';
import { useGetJobPinsArticles } from '@pages/jobPins/apis/queries';
import Footer from '@pages/myBookmark/components/footer/Footer';

const MOCK_JOB_PINS = Array.from({ length: 30 }, (_, index) => ({
id: index + 1,
title: '텍스트텍스트텍스트텍스트텍스트텍스트텍스트',
content: '서브텍스트입니다서브텍스트입니다서브텍스트입니다서브텍스트입니다',
category: '카테고리명',
categoryColor: 'COLOR7' as const,
variant: 'save' as const,
nickname: index % 3 === 0 ? '구글닉네임명' : '',
}));
import { Card } from '@pinback/design-system/ui';
import { useInfiniteScroll } from '@shared/hooks/useInfiniteScroll';
import { useRef } from 'react';

const JobPins = () => {
const scrollContainerRef = useRef<HTMLDivElement>(null);

const { data, isPending, fetchNextPage, hasNextPage } =
useGetJobPinsArticles();
Comment on lines +10 to +11
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

에러 상태 처리 누락

useGetJobPinsArticles에서 isError를 destructure하지 않아 API 호출 실패 시 사용자에게 적절한 피드백을 제공할 수 없습니다.

🛡️ 에러 처리 추가 제안
-  const { data, isPending, fetchNextPage, hasNextPage } =
+  const { data, isPending, isError, fetchNextPage, hasNextPage } =
     useGetJobPinsArticles();

그리고 렌더링 부분에 에러 상태 처리를 추가하세요:

if (isError) {
  return <div>아티클을 불러오는데 실패했습니다.</div>;
}
📝 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 { data, isPending, fetchNextPage, hasNextPage } =
useGetJobPinsArticles();
const { data, isPending, isError, fetchNextPage, hasNextPage } =
useGetJobPinsArticles();
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/client/src/pages/jobPins/JobPins.tsx` around lines 10 - 11, Destructure
isError from the hook call (const { data, isPending, fetchNextPage, hasNextPage,
isError } = useGetJobPinsArticles()) and add an early render branch in JobPins
(e.g., if (isError) return <div>아티클을 불러오는데 실패했습니다.</div>;), so API failures from
useGetJobPinsArticles are surfaced to the user instead of silently failing.


const observerRef = useInfiniteScroll({
fetchNextPage,
hasNextPage,
root: scrollContainerRef,
});

const articlesToDisplay =
data?.pages.flatMap((page) => page.articles ?? []) ?? [];

const job = data?.pages?.[0]?.job ?? null;

if (isPending) {
return <div>Loading...</div>;
}

return (
<div className="flex h-screen flex-col pl-[8rem] pr-[5rem] pt-[5.2rem]">
<div className="flex items-center gap-[1.2rem]">
<p className="head3">관심 직무 핀</p>
<p className="head3 text-main500">기획자</p>
{job && <p className="head3 text-main500">{job}</p>}
</div>
<p className="body3-r text-font-gray-3 mt-[0.8rem]">
같은 직무의 사람들이 저장한 아티클을 살펴봐요. 선택한 직무를 기준으로
최신 핀이 업데이트 돼요!
</p>

<div className="scrollbar-hide mt-[2.6rem] flex h-screen flex-wrap content-start gap-[1.6rem] overflow-y-auto scroll-smooth">
{MOCK_JOB_PINS.map((pin) => (
<Card
key={pin.id}
type="bookmark"
variant={pin.variant}
title={pin.title}
content={pin.content}
category={pin.category}
categoryColor={pin.categoryColor}
nickname={pin.nickname}
/>
))}
</div>
{job === null ? (
<p className="body2-m text-font-gray-3 mt-[4rem]">
기존 사용자 직무 선택 API로 직무 정보를 변경해주세요.
</p>
) : articlesToDisplay.length > 0 ? (
<div
ref={scrollContainerRef}
className="scrollbar-hide mt-[2.6rem] flex h-screen flex-wrap content-start gap-[1.6rem] overflow-y-auto scroll-smooth"
>
{articlesToDisplay.map((article) => (
<Card
key={article.articleId}
type="bookmark"
variant="save"
title={article.title}
imageUrl={article.thumbnailUrl}
content={article.memo}
category={article.category.categoryName}
categoryColor={article.category.categoryColor}
nickname={article.ownerName}
onClick={() => window.open(article.url, '_blank')}
/>
))}

<div ref={observerRef} style={{ height: '1px', width: '100%' }} />
</div>
) : (
// TODO: 아티클 없는경우 UI 수정
<p className="body2-m text-font-gray-3 mt-[4rem]">
아직 공유된 아티클이 없어요.
</p>
Comment on lines +66 to +69
Copy link
Collaborator

Choose a reason for hiding this comment

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

이 부분은 아직 디자인이 안나온건가요?

Copy link
Member Author

Choose a reason for hiding this comment

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

네넵 지금 다른 북마크/리마인드 아티클 없을 때 뷰는 있는데 여기 직무 부분은 따로 없네요.
생각보다 다른 느낌의 기능이라 같은 UI가 재사용되는 것은 아닐 것 같아서 디자인한테 문의 드려보고 반영하겠습니다~

)}

<Footer />
</div>
Expand Down
22 changes: 22 additions & 0 deletions apps/client/src/pages/jobPins/apis/axios.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import apiRequest from '@shared/apis/setting/axiosInstance';
import { JobPinsResponse } from '@pages/jobPins/types/api';

interface ApiResponse<T> {
code: string;
message: string;
data: T;
}

export const getJobPinsArticles = async (
page: number,
size: number
): Promise<JobPinsResponse> => {
const { data } = await apiRequest.get('/api/v3/articles/shared/job', {
params: {
page,
size,
},
});

return (data as ApiResponse<JobPinsResponse>).data;
};
18 changes: 18 additions & 0 deletions apps/client/src/pages/jobPins/apis/queries.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { useInfiniteQuery } from '@tanstack/react-query';
import { JobPinsResponse } from '@pages/jobPins/types/api';
import { getJobPinsArticles } from './axios';

export const useGetJobPinsArticles = () => {
return useInfiniteQuery<JobPinsResponse>({
queryKey: ['jobPinsArticles'],
queryFn: ({ pageParam = 0 }) => getJobPinsArticles(pageParam as number, 20),
initialPageParam: 0,
getNextPageParam: (lastPage, allPages) => {
if (lastPage.articles.length === 0) {
return undefined;
}

return allPages.length;
},
});
};
20 changes: 20 additions & 0 deletions apps/client/src/pages/jobPins/types/api.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
interface JobPinCategory {
categoryId: number;
categoryName: string;
categoryColor: string;
}

interface JobPinArticle {
articleId: number;
url: string;
title: string;
thumbnailUrl: string;
memo: string;
ownerName: string;
category: JobPinCategory;
}

export interface JobPinsResponse {
job: string | null;
articles: JobPinArticle[];
}
Loading