11'use client' ;
22import Image from 'next/image' ;
33import Link from 'next/link' ;
4- import { useState } from 'react' ;
54import { FaGithub , FaGlobe } from 'react-icons/fa' ;
5+ import { MdArrowForward } from 'react-icons/md' ;
66import { Project } from '@/app/types/Portfolio' ;
77
88interface PortfolioPreviewProps {
99 project : Project ;
1010 hideTags ?: boolean ;
11- hoverEffect ?: boolean ;
1211}
1312
14- const PortfolioPreview = ( {
15- project,
16- hideTags,
17- hoverEffect = true ,
18- } : PortfolioPreviewProps ) => {
19- const [ isHovered , setIsHovered ] = useState ( false ) ;
20- const [ isTouched , setIsTouched ] = useState ( false ) ;
21-
22- const handleTouchStart = ( ) => {
23- if ( hoverEffect ) {
24- setIsTouched ( true ) ;
25- }
26- } ;
27-
28- const handleTouchEnd = ( ) => {
29- if ( hoverEffect ) {
30- setTimeout ( ( ) => setIsTouched ( false ) , 3000 ) ;
31- }
32- } ;
33-
34- const showOverlay = hoverEffect && ( isHovered || isTouched ) ;
35-
13+ const PortfolioPreview = ( { project, hideTags } : PortfolioPreviewProps ) => {
3614 return (
37- < div
38- className = "group bg-gradient-to-br from-gray-50 to-gray-100 dark:from-primary-rich rounded-2xl overflow-hidden shadow transition-all duration-300 border border-gray-200 dark:border-gray-700"
39- onMouseEnter = { ( ) => setIsHovered ( true ) }
40- onMouseLeave = { ( ) => setIsHovered ( false ) }
41- >
42- < div
43- className = "relative overflow-hidden"
44- onTouchStart = { handleTouchStart }
45- onTouchEnd = { handleTouchEnd }
46- >
47- < Image
48- width = { 500 }
49- height = { 400 }
50- src = { project . image }
51- alt = { `${ project . title } 프로젝트 이미지` }
52- className = { `w-full aspect-video object-cover transition-transform duration-500 ${
53- showOverlay ? 'scale-110' : 'scale-100'
54- } `}
55- />
56-
57- { /* 데스크톱 호버 오버레이 */ }
58- < div
59- className = { `absolute inset-0 bg-black bg-opacity-50 hidden md:flex items-center justify-center transition-opacity duration-300 ${
60- isHovered ? 'opacity-100' : 'opacity-0'
61- } `}
62- >
63- < div className = "flex gap-3 " >
64- { project . demoUrl && (
65- < Link
66- href = { project . demoUrl }
67- target = "_blank"
68- rel = "noopener noreferrer"
69- >
70- < button className = "inline-flex items-center gap-2 bg-neutral-500 hover:bg-neutral-600 text-white px-4 py-2 rounded-md transition-colors text-sm " >
71- 배포 < FaGlobe size = { 14 } />
72- </ button >
73- </ Link >
74- ) }
75- { project . githubUrl && (
76- < Link
77- href = { project . githubUrl }
78- target = "_blank"
79- rel = "noopener noreferrer"
80- className = { 'h-full' }
81- >
82- < button className = "inline-flex items-center gap-2 bg-gray-700 hover:bg-gray-800 text-white px-4 py-2 rounded-md transition-colors text-sm " >
83- < FaGithub size = { 14 } /> Github
84- </ button >
85- </ Link >
86- ) }
87- { project . slug && (
88- < Link
89- href = { `/portfolio/${ project . slug } ` }
90- rel = "noopener noreferrer"
91- >
92- < button className = "bg-neutral-700 hover:bg-neutral-800 text-white px-4 py-2 rounded-md transition-colors text-sm" >
93- 세부 정보
94- </ button >
95- </ Link >
96- ) }
97- </ div >
98- </ div >
99-
100- { /* 모바일 터치 오버레이 */ }
101- < div
102- className = { `absolute inset-0 bg-black bg-opacity-50 flex md:hidden items-center justify-center transition-opacity duration-300 ${
103- isTouched ? 'opacity-100' : 'opacity-0'
104- } `}
105- >
106- < div className = "flex flex-col gap-2 px-4 " >
107- { project . demoUrl && (
108- < Link
109- href = { project . demoUrl }
110- target = "_blank"
111- rel = "noopener noreferrer"
112- >
113- < button className = "inline-flex items-center justify-center gap-2 w-full bg-neutral-500 hover:bg-neutral-600 text-white px-4 py-3 rounded-md transition-colors text-sm" >
114- 배포 보기 < FaGlobe size = { 14 } />
115- </ button >
116- </ Link >
117- ) }
118- { project . githubUrl && (
119- < Link
120- href = { project . githubUrl }
121- target = "_blank"
122- rel = "noopener noreferrer"
123- >
124- < button className = "inline-flex items-center justify-center gap-2 w-full bg-gray-700 hover:bg-gray-800 text-white px-4 py-3 rounded-md transition-colors text-sm " >
125- GitHub < FaGithub size = { 14 } />
126- </ button >
127- </ Link >
128- ) }
129- { project . slug && (
130- < Link
131- href = { `/portfolio/${ project . slug } ` }
132- rel = "noopener noreferrer"
133- >
134- < button className = "w-full bg-neutral-700 hover:bg-neutral-800 text-white px-4 py-3 rounded-md transition-colors text-sm" >
135- 세부 정보
136- </ button >
137- </ Link >
138- ) }
139- </ div >
140- </ div >
141- </ div >
142-
143- < div className = "p-6 md:p-8" >
15+ < div className = "flex flex-col md:flex-row group bg-gradient-to-br from-gray-50 to-gray-100 dark:from-primary-rich rounded-2xl overflow-hidden shadow transition-all duration-300 border border-gray-200 dark:border-gray-700" >
16+ { /* 이미지 영역 */ }
17+ < div className = "relative overflow-hidden w-full md:w-1/3 shrink-0" >
14418 < Link href = { project . slug ? `/portfolio/${ project . slug } ` : '#' } >
145- < h3 className = "text-xl md:text-2xl font-bold mb-3 text-gray-900 dark:text-gray-100 hover:text-gray-600 dark:hover:text-gray-400 transition-colors line-clamp-2" >
146- { project . title }
147- </ h3 >
19+ < Image
20+ width = { 500 }
21+ height = { 400 }
22+ src = { project . image }
23+ alt = { `${ project . title } 프로젝트 이미지` }
24+ className = "w-full h-48 md:h-full object-cover transition-transform duration-500 group-hover:scale-105"
25+ />
14826 </ Link >
149- < p className = "text-sm md:text-base text-gray-600 dark:text-gray-300 mb-4 md:mb-5 line-clamp-3 leading-relaxed" >
150- { project . description }
151- </ p >
27+ </ div >
15228
153- { ! hideTags && project . tags && project . tags . length > 0 && (
154- < div className = "flex flex-wrap gap-2 mt-4 " >
155- { project . tags . map ( ( tag , index ) => (
156- < span
157- key = { index }
158- className = "text-xs px-3 py-1.5 bg- gray-200 dark:bg -gray-700 rounded-full text-gray-700 dark:text-gray-300 whitespace-nowrap font-medium"
159- >
160- { tag }
161- </ span >
162- ) ) }
163- </ div >
164- ) }
29+ { /* 정보 + 버튼 영역 */ }
30+ < div className = "flex flex-1 flex-col md:flex-row " >
31+ { /* 텍스트 정보 */ }
32+ < div className = "flex-1 p-5 md:p-6" >
33+ < Link href = { project . slug ? `/portfolio/ ${ project . slug } ` : '#' } >
34+ < h3 className = "text-lg md:text-xl font-bold mb-2 text- gray-900 dark:text -gray-100 hover: text-gray-600 dark:hover: text-gray-400 transition-colors line-clamp-2" >
35+ { project . title }
36+ </ h3 >
37+ </ Link >
38+ < p className = "text-sm text-gray-600 dark:text-gray-300 mb-3 line-clamp-3 leading-relaxed" >
39+ { project . description }
40+ </ p >
16541
166- { /* 모바일 하단 액션 버튼들 */ }
167- < div className = "flex md:hidden gap-2 mt-5 pt-4 border-t border-gray-200 dark:border-gray-700" >
42+ { ! hideTags && project . tags && project . tags . length > 0 && (
43+ < div className = "flex flex-wrap gap-1.5" >
44+ { project . tags . map ( ( tag , index ) => (
45+ < span
46+ key = { index }
47+ className = "text-xs px-2.5 py-1 bg-gray-200 dark:bg-gray-700 rounded-full text-gray-700 dark:text-gray-300 whitespace-nowrap font-medium"
48+ >
49+ { tag }
50+ </ span >
51+ ) ) }
52+ </ div >
53+ ) }
54+ </ div >
55+ < div className = "flex md:flex-col gap-2 p-4 md:p-5 border-t md:border-t-0 md:border-l border-gray-200 dark:border-gray-700 justify-center items-stretch" >
16856 { project . demoUrl && (
16957 < Link
17058 href = { project . demoUrl }
17159 target = "_blank"
17260 rel = "noopener noreferrer"
173- className = "flex-1"
61+ className = "flex-1 md:flex-none"
62+ title = "배포 링크"
17463 >
175- < button className = "inline-flex items-center justify-center gap-1.5 w-full bg-gray-200 dark:bg-gray-700 hover:bg-gray-300 dark:hover:bg-gray-600 text-gray-800 dark:text-gray-200 px-3 py-2.5 rounded-lg transition-colors text-xs font-medium" >
64+ < button className = "inline-flex items-center justify-center gap-1.5 w-full bg-gray-200 dark:bg-gray-700 hover:bg-gray-300 dark:hover:bg-gray-600 text-gray-800 dark:text-gray-200 px-3 py-2 rounded-lg transition-colors text-xs font-medium" >
17665 < FaGlobe size = { 12 } />
17766 배포
17867 </ button >
@@ -183,9 +72,10 @@ const PortfolioPreview = ({
18372 href = { project . githubUrl }
18473 target = "_blank"
18574 rel = "noopener noreferrer"
186- className = "flex-1"
75+ className = "flex-1 md:flex-none"
76+ title = "GitHub 코드 보기"
18777 >
188- < button className = "inline-flex items-center justify-center gap-1.5 w-full bg-gray-200 dark:bg-gray-700 hover:bg-gray-300 dark:hover:bg-gray-600 text-gray-800 dark:text-gray-200 px-3 py-2.5 rounded-lg transition-colors text-xs font-medium" >
78+ < button className = "inline-flex items-center justify-center gap-1.5 w-full bg-gray-200 dark:bg-gray-700 hover:bg-gray-300 dark:hover:bg-gray-600 text-gray-800 dark:text-gray-200 px-3 py-2 rounded-lg transition-colors text-xs font-medium" >
18979 < FaGithub size = { 12 } />
19080 코드
19181 </ button >
@@ -195,9 +85,11 @@ const PortfolioPreview = ({
19585 < Link
19686 href = { `/portfolio/${ project . slug } ` }
19787 rel = "noopener noreferrer"
198- className = "flex-1"
88+ className = "flex-1 md:flex-none"
89+ title = "자세히 보기"
19990 >
200- < button className = "w-full bg-gray-800 dark:bg-gray-200 hover:bg-gray-700 dark:hover:bg-gray-300 text-gray-100 dark:text-gray-900 px-3 py-2.5 rounded-lg transition-colors text-xs font-medium" >
91+ < button className = "inline-flex items-center justify-center gap-1.5 w-full bg-gray-800 dark:bg-gray-200 hover:bg-gray-700 dark:hover:bg-gray-300 text-gray-100 dark:text-gray-900 px-3 py-2 rounded-lg transition-colors text-xs font-medium" >
92+ < MdArrowForward size = { 12 } />
20193 자세히
20294 </ button >
20395 </ Link >
0 commit comments