From a600503429b9c69772386b5736b1af47b1ec68bb Mon Sep 17 00:00:00 2001 From: velopert Date: Wed, 5 Feb 2025 18:56:48 +0900 Subject: [PATCH] fix: job positions logic --- src/components/post/JobPositions.tsx | 34 +++++++++++++++++--------- src/containers/post/PostViewer.tsx | 36 +++++++++++++++++++++------- src/lib/api/jobs.ts | 21 ++++++++++++++++ 3 files changed, 71 insertions(+), 20 deletions(-) create mode 100644 src/lib/api/jobs.ts diff --git a/src/components/post/JobPositions.tsx b/src/components/post/JobPositions.tsx index a0471e66..b6869448 100644 --- a/src/components/post/JobPositions.tsx +++ b/src/components/post/JobPositions.tsx @@ -8,6 +8,7 @@ import { themedPalette } from '../../lib/styles/themes'; import { ellipsis } from '../../lib/styles/utils'; import media from '../../lib/styles/media'; import gtag from '../../lib/gtag'; +import { getJobs, Job } from '../../lib/api/jobs'; type Props = { category: 'frontend' | 'backend' | 'mobile' | 'python' | 'node' | 'ai' | null; @@ -15,16 +16,19 @@ type Props = { function JobPositions({ category }: Props) { const [isObserved, setIsObserved] = useState(false); - const { data } = useQuery<{ jobPositions: JobPosition[] }>(JOB_POSITIONS, { - variables: { - category: category ?? undefined, - }, - skip: !isObserved, - }); + const [data, setData] = useState([]); const ref = useRef(null); const initializedRef = useRef(false); + useEffect(() => { + getJobs(category || 'general').then((jobs) => { + const shuffled = jobs.sort(() => Math.random() - 0.5); + const sliced = shuffled.slice(0, 3); + setData(sliced); + }); + }, [category]); + useEffect(() => { const observer = new IntersectionObserver( (entries) => { @@ -57,7 +61,7 @@ function JobPositions({ category }: Props) { gtag('event', 'job_position_view'); }, [isObserved]); - if (!data?.jobPositions) + if (!data) return (
@@ -70,7 +74,7 @@ function JobPositions({ category }: Props) {

관련 채용 정보

- {data.jobPositions.map((jobPosition) => ( + {data.map((jobPosition) => ( @@ -84,6 +88,7 @@ function JobPositions({ category }: Props) { {jobPosition.name} + {jobPosition.summary} ))} @@ -122,10 +127,10 @@ const Container = styled.div` `; const Card = styled.div` - width: 25%; + width: 33.33%; ${media.small} { flex-shrink: 0; - width: 27vw; + width: 60vw; } `; @@ -152,9 +157,16 @@ const Company = styled.div` `; const JobTitle = styled.a` - font-size: 12px; + font-size: 14px; font-weight: 600; line-height: 1.25; `; +const JobDescription = styled.div` + margin-top: 8px; + color: ${themedPalette.text2}; + font-size: 12px; + line-height: 1.5; +`; + export default JobPositions; diff --git a/src/containers/post/PostViewer.tsx b/src/containers/post/PostViewer.tsx index 9b5d75e7..34a1e936 100644 --- a/src/containers/post/PostViewer.tsx +++ b/src/containers/post/PostViewer.tsx @@ -223,7 +223,7 @@ const PostViewer: React.FC = ({ const isOwnPost = post.user.id === userId; const isVeryOld = Date.now() - new Date(post.released_at).getTime() > - 1000 * 60 * 60 * 24 * 30; + 1000 * 60 * 60 * 24 * 10; if (isOwnPost) return false; if (!isVeryOld) return false; @@ -254,16 +254,24 @@ const PostViewer: React.FC = ({ }, [customAd, shouldShowBanner, shouldShowFooterBanner]); const category = useMemo(() => { - const frontendKeywords = ['프런트엔드', '리액트', 'vue', 'react', 'next']; + const frontendKeywords = [ + '프런트엔드', + '리액트', + 'vue', + 'react', + 'next', + '프론트엔드', + ]; const backendKeywords = ['백엔드', '서버', '데이터베이스', 'db']; - const aiKeywords = ['인공지능', '머신러닝', '딥러닝', 'ai']; + const aiKeywords = ['인공지능', '머신러닝', '딥러닝', 'nlp', 'llm']; const mobileKeywords = [ - '모바일', '안드로이드', 'ios', 'react native', '플러터', 'flutter', + 'swift', + 'xcode', ]; const pythonKeywords = ['파이썬', 'python']; const nodeKeywords = ['노드', 'node', 'express', 'koa', 'nest']; @@ -274,15 +282,25 @@ const PostViewer: React.FC = ({ .concat(post.tags.join(',')) .concat(post.body) .toLowerCase(); + if ( + aiKeywords.some((keyword) => { + const value = merged.includes(keyword); + if (value) { + console.log(merged); + console.log(keyword); + } + return value; + }) + ) + return 'ai'; if (frontendKeywords.some((keyword) => merged.includes(keyword))) return 'frontend'; - if (backendKeywords.some((keyword) => merged.includes(keyword))) - return 'backend'; - if (aiKeywords.some((keyword) => merged.includes(keyword))) return 'ai'; if (mobileKeywords.some((keyword) => merged.includes(keyword))) return 'mobile'; if (pythonKeywords.some((keyword) => merged.includes(keyword))) return 'python'; + if (backendKeywords.some((keyword) => merged.includes(keyword))) + return 'backend'; if (nodeKeywords.some((keyword) => merged.includes(keyword))) return 'node'; return null; }, [data]); @@ -522,10 +540,10 @@ const PostViewer: React.FC = ({ /> - {shouldShowBanner && isContentLongEnough && customAd ? ( + {shouldShowBanner && isContentLongEnough ? ( ) : null} - {shouldShowFooterBanner && customAd ? ( + {shouldShowFooterBanner ? ( ) : null} (`/jobs/${category}`); + return response.data; +} + +export type Job = { + id: number; + name: string; + companyName: string; + companyLogo: string; + thumbnail: string; + url: string; + jobId: number; + summary: string; +};