diff --git a/apps/client/src/pages/jobPins/JobPins.tsx b/apps/client/src/pages/jobPins/JobPins.tsx
index 46b31f3..2357180 100644
--- a/apps/client/src/pages/jobPins/JobPins.tsx
+++ b/apps/client/src/pages/jobPins/JobPins.tsx
@@ -1,5 +1,9 @@
-import { useGetJobPinsArticles } from '@pages/jobPins/apis/queries';
+import {
+ useGetJobPinsArticleDetail,
+ useGetJobPinsArticles,
+} from '@pages/jobPins/apis/queries';
import JobPinsBottomNotice from '@pages/jobPins/components/JobPinsBottomNotice';
+import MemoPopup from '@pages/jobPins/components/MemoPopup';
import { useJobPinsBottomNotice } from '@pages/jobPins/hooks/useJobPinsBottomNotice';
import Footer from '@pages/myBookmark/components/footer/Footer';
import { Card } from '@pinback/design-system/ui';
@@ -11,6 +15,11 @@ const JobPins = () => {
const { data, isPending, fetchNextPage, hasNextPage } =
useGetJobPinsArticles();
+ const {
+ mutate: getJobPinDetail,
+ data: jobPinDetail,
+ reset: resetJobPinDetail,
+ } = useGetJobPinsArticleDetail();
const observerRef = useInfiniteScroll({
fetchNextPage,
@@ -33,6 +42,7 @@ const JobPins = () => {
관심 직무 핀
{job && {job}
}
+
같은 직무의 사람들이 저장한 아티클을 살펴봐요. 선택한 직무를 기준으로
최신 핀이 업데이트 돼요!
@@ -48,32 +58,49 @@ const JobPins = () => {
onWheel={handleBottomWheel}
className="scrollbar-hide mt-[2.6rem] flex h-screen flex-wrap content-start gap-[1.6rem] overflow-y-auto scroll-smooth"
>
- {articlesToDisplay.map((article) => (
- window.open(article.url, '_blank')}
- />
- ))}
+ {articlesToDisplay.map((article) => {
+ const displayTitle = article.title?.trim()
+ ? article.title
+ : '제목 없음';
+
+ const displayImageUrl = article.thumbnailUrl || undefined;
+
+ return (
+ getJobPinDetail(article.articleId)}
+ />
+ );
+ })}
) : (
- // TODO: 아티클 없는경우 UI 수정
아직 공유된 아티클이 없어요.
)}
+
+ {/* Memo Popup */}
+ {jobPinDetail && (
+ window.open(jobPinDetail.url, '_blank')}
+ />
+ )}
);
};
diff --git a/apps/client/src/pages/jobPins/apis/axios.ts b/apps/client/src/pages/jobPins/apis/axios.ts
index 0d7218e..292a3b5 100644
--- a/apps/client/src/pages/jobPins/apis/axios.ts
+++ b/apps/client/src/pages/jobPins/apis/axios.ts
@@ -1,5 +1,5 @@
-import apiRequest from '@shared/apis/setting/axiosInstance';
import { JobPinsResponse } from '@pages/jobPins/types/api';
+import apiRequest from '@shared/apis/setting/axiosInstance';
interface ApiResponse {
code: string;
@@ -20,3 +20,20 @@ export const getJobPinsArticles = async (
return (data as ApiResponse).data;
};
+
+export interface JobPinsDetailResponse {
+ articleId: number;
+ ownerName: string;
+ memo: string | null;
+ url: string;
+}
+
+export const getJobPinsArticleDetail = async (
+ articleId: number
+): Promise => {
+ const { data } = await apiRequest.get(
+ `/api/v3/articles/shared/job/${articleId}`
+ );
+
+ return (data as ApiResponse).data;
+};
diff --git a/apps/client/src/pages/jobPins/apis/queries.ts b/apps/client/src/pages/jobPins/apis/queries.ts
index c279c1d..3a9894b 100644
--- a/apps/client/src/pages/jobPins/apis/queries.ts
+++ b/apps/client/src/pages/jobPins/apis/queries.ts
@@ -1,6 +1,10 @@
-import { useInfiniteQuery } from '@tanstack/react-query';
import { JobPinsResponse } from '@pages/jobPins/types/api';
-import { getJobPinsArticles } from './axios';
+import { useInfiniteQuery, useMutation } from '@tanstack/react-query';
+import {
+ getJobPinsArticleDetail,
+ getJobPinsArticles,
+ JobPinsDetailResponse,
+} from './axios';
export const useGetJobPinsArticles = () => {
return useInfiniteQuery({
@@ -16,3 +20,9 @@ export const useGetJobPinsArticles = () => {
},
});
};
+
+export const useGetJobPinsArticleDetail = () => {
+ return useMutation({
+ mutationFn: (articleId: number) => getJobPinsArticleDetail(articleId),
+ });
+};
diff --git a/apps/client/src/pages/jobPins/components/MemoPopup.tsx b/apps/client/src/pages/jobPins/components/MemoPopup.tsx
new file mode 100644
index 0000000..e97eea5
--- /dev/null
+++ b/apps/client/src/pages/jobPins/components/MemoPopup.tsx
@@ -0,0 +1,51 @@
+import { Icon } from '@pinback/design-system/icons';
+import { Button } from '@pinback/design-system/ui';
+
+interface MemoPopupProps {
+ userName: string;
+ memo?: string | null;
+ onClose: () => void;
+ onGoArticle: () => void;
+}
+
+export default function MemoPopup({
+ userName,
+ memo,
+ onClose,
+ onGoArticle,
+}: MemoPopupProps) {
+ const hasMemo = memo && memo.trim().length > 0;
+
+ return (
+
+
+ {/* Header */}
+
+
+
+
+
+ {/* Title */}
+
+ {userName}님의 메모
+
+
+ {/* Memo */}
+
+ {hasMemo ? (
+ memo
+ ) : (
+
작성된 메모가 없어요.
+ )}
+
+
+ {/* Button */}
+
+
+
+ );
+}