@@ -14,8 +14,9 @@ import {
1414 DialogDescription ,
1515 DialogFooter
1616} from '@/components/ui/dialog' ;
17- import { Sparkles , Frame , Eye , Heart , ArrowLeftRight , Trophy } from 'lucide-react' ;
17+ import { Sparkles , Frame , Eye , Heart , ArrowLeftRight , Trophy , Share2 , ChevronDown , ChevronUp } from 'lucide-react' ;
1818import { exhibitionsData , type Exhibition } from '@/data/exhibitions' ;
19+ import { MarkdownRenderer } from '@/components/MarkdownRenderer' ;
1920import heroArtwork from '@/assets/hero-artwork.jpg' ;
2021
2122interface Artwork {
@@ -41,6 +42,7 @@ const Index = () => {
4142 const [ savedMessagesCount , setSavedMessagesCount ] = useState ( 0 ) ;
4243 const [ savedMessages , setSavedMessages ] = useState < Message [ ] > ( [ ] ) ;
4344 const [ showSavedModal , setShowSavedModal ] = useState ( false ) ;
45+ const [ expandedMessages , setExpandedMessages ] = useState < Set < string > > ( new Set ( ) ) ;
4446
4547 const exhibitions = exhibitionsData ;
4648
@@ -100,6 +102,81 @@ const Index = () => {
100102 setSavedMessages ( messages ) ;
101103 } ;
102104
105+ const handleShareMessage = async ( message : Message ) => {
106+ const shareText = `Artemia AI 추천:\n\n${ message . content } \n\n전시 추천 서비스 - Artemia: Art Curator AI` ;
107+
108+ try {
109+ if ( navigator . share && navigator . canShare ( { text : shareText } ) ) {
110+ await navigator . share ( {
111+ title : 'Artemia AI 전시 추천' ,
112+ text : shareText
113+ } ) ;
114+ } else {
115+ await navigator . clipboard . writeText ( shareText ) ;
116+ // You could add a toast notification here
117+ alert ( '메시지가 클립보드에 복사되었습니다!' ) ;
118+ }
119+ } catch ( err ) {
120+ console . error ( 'Share failed:' , err ) ;
121+ // Fallback to clipboard
122+ try {
123+ await navigator . clipboard . writeText ( shareText ) ;
124+ alert ( '메시지가 클립보드에 복사되었습니다!' ) ;
125+ } catch ( clipboardErr ) {
126+ console . error ( 'Clipboard copy failed:' , clipboardErr ) ;
127+ }
128+ }
129+ } ;
130+
131+ const handleShareAllMessages = async ( ) => {
132+ if ( savedMessages . length === 0 ) return ;
133+
134+ const allMessagesText = savedMessages
135+ . map ( ( msg , index ) => `${ index + 1 } . ${ msg . content } ` )
136+ . join ( '\n\n' ) ;
137+
138+ const shareText = `Artemia AI 저장된 추천들:\n\n${ allMessagesText } \n\n전시 추천 서비스 - Artemia: Art Curator AI` ;
139+
140+ try {
141+ if ( navigator . share && navigator . canShare ( { text : shareText } ) ) {
142+ await navigator . share ( {
143+ title : `Artemia AI 저장된 메시지 ${ savedMessages . length } 개` ,
144+ text : shareText
145+ } ) ;
146+ } else {
147+ await navigator . clipboard . writeText ( shareText ) ;
148+ alert ( '모든 메시지가 클립보드에 복사되었습니다!' ) ;
149+ }
150+ } catch ( err ) {
151+ console . error ( 'Share failed:' , err ) ;
152+ try {
153+ await navigator . clipboard . writeText ( shareText ) ;
154+ alert ( '모든 메시지가 클립보드에 복사되었습니다!' ) ;
155+ } catch ( clipboardErr ) {
156+ console . error ( 'Clipboard copy failed:' , clipboardErr ) ;
157+ }
158+ }
159+ } ;
160+
161+ const toggleMessageExpansion = ( messageId : string ) => {
162+ setExpandedMessages ( prev => {
163+ const newSet = new Set ( prev ) ;
164+ if ( newSet . has ( messageId ) ) {
165+ newSet . delete ( messageId ) ;
166+ } else {
167+ newSet . add ( messageId ) ;
168+ }
169+ return newSet ;
170+ } ) ;
171+ } ;
172+
173+ const getMessagePreview = ( content : string , maxLength : number = 200 ) => {
174+ if ( content . length <= maxLength ) return content ;
175+ return content . substring ( 0 , maxLength ) + '...' ;
176+ } ;
177+
178+ const isLongMessage = ( content : string ) => content . length > 200 ;
179+
103180 return (
104181 < div className = "h-screen bg-background flex flex-col overflow-hidden" >
105182 { /* Hero Section */ }
@@ -123,9 +200,6 @@ const Index = () => {
123200 </ h1 >
124201 </ div >
125202
126- < p className = "text-sm sm:text-base lg:text-lg text-primary-foreground/80 mb-3 sm:mb-4 lg:mb-6 leading-relaxed" >
127- 전시 추천 주변 관광지 추천 서비스
128- </ p >
129203
130204 < div className = "flex flex-wrap justify-center gap-2 sm:gap-3 lg:gap-4" >
131205 < Button
@@ -203,40 +277,112 @@ const Index = () => {
203277 />
204278 ) }
205279
206- { /* Saved Messages Modal */ }
280+ { /* Saved Messages Modal - Fullscreen */ }
207281 < Dialog open = { showSavedModal } onOpenChange = { setShowSavedModal } >
208- < DialogContent className = "max-w-lg " >
209- < DialogHeader >
210- < DialogTitle > Saved Messages </ DialogTitle >
211- < DialogDescription >
212- These are the messages you've marked as favorite .
282+ < DialogContent className = "max-w-none w-full h-full max-h-none p-0 gap-0 " >
283+ < DialogHeader className = "p-4 sm:p-6 border-b text-center" >
284+ < DialogTitle className = "text-xl sm:text-2xl font-medium text-center" > 저장된 메시지 </ DialogTitle >
285+ < DialogDescription className = "text-center" >
286+ 즐겨찾기로 저장한 메시지들을 확인하고 공유할 수 있습니다 .
213287 </ DialogDescription >
214288 </ DialogHeader >
215289
216- < div className = "space-y-3 max-h-[300px] overflow-y-auto" >
217- { savedMessages . length > 0 ? (
218- savedMessages . map ( ( msg ) => (
219- < div
220- key = { msg . id }
221- className = "p-3 border rounded-lg bg-accent/5 text-sm"
222- >
223- { msg . content }
290+ < div className = "flex-1 p-4 sm:p-6 overflow-y-auto" >
291+ < div className = "space-y-4 max-w-4xl mx-auto" >
292+ { savedMessages . length > 0 ? (
293+ savedMessages . map ( ( msg ) => {
294+ const isExpanded = expandedMessages . has ( msg . id ) ;
295+ const isLong = isLongMessage ( msg . content ) ;
296+ const contentToShow = isLong && ! isExpanded
297+ ? getMessagePreview ( msg . content )
298+ : msg . content ;
299+
300+ return (
301+ < div
302+ key = { msg . id }
303+ className = { `border rounded-xl bg-gradient-to-r from-card to-accent/5 shadow-sm transition-all duration-300 ${
304+ isLong && ! isExpanded
305+ ? 'p-3 sm:p-4'
306+ : 'p-4 sm:p-6'
307+ } `}
308+ >
309+ < MarkdownRenderer
310+ content = { contentToShow }
311+ className = { isLong && ! isExpanded ? "text-xs sm:text-sm" : "text-sm sm:text-base" }
312+ />
313+
314+ { isLong && (
315+ < div className = "mt-3" >
316+ < Button
317+ variant = "ghost"
318+ size = "sm"
319+ onClick = { ( ) => toggleMessageExpansion ( msg . id ) }
320+ className = "text-xs text-primary hover:text-primary/80 p-0 h-auto"
321+ >
322+ { isExpanded ? (
323+ < >
324+ < ChevronUp className = "w-3 h-3 mr-1" />
325+ 접기
326+ </ >
327+ ) : (
328+ < >
329+ < ChevronDown className = "w-3 h-3 mr-1" />
330+ 더보기
331+ </ >
332+ ) }
333+ </ Button >
334+ </ div >
335+ ) }
336+
337+ < div className = "mt-4 flex items-center justify-between text-xs sm:text-sm text-muted-foreground" >
338+ < span > { new Date ( msg . timestamp ) . toLocaleString ( 'ko-KR' ) } </ span >
339+ < Button
340+ variant = "ghost"
341+ size = "sm"
342+ onClick = { ( ) => handleShareMessage ( msg ) }
343+ className = "text-xs"
344+ >
345+ < Share2 className = "w-3 h-3 mr-1" />
346+ 공유
347+ </ Button >
348+ </ div >
349+ </ div >
350+ ) ;
351+ } )
352+ ) : (
353+ < div className = "text-center py-12" >
354+ < Heart className = "w-16 h-16 mx-auto text-muted-foreground/20 mb-4" />
355+ < p className = "text-muted-foreground text-lg mb-2" >
356+ 저장된 메시지가 없습니다
357+ </ p >
358+ < p className = "text-muted-foreground/60 text-sm" >
359+ AI 응답에서 ❤️ 버튼을 눌러 메시지를 저장해보세요
360+ </ p >
224361 </ div >
225- ) )
226- ) : (
227- < p className = "text-muted-foreground text-sm" >
228- No saved messages yet.
229- </ p >
230- ) }
362+ ) }
363+ </ div >
231364 </ div >
232365
233- < DialogFooter >
234- < Button
235- variant = "secondary"
236- onClick = { ( ) => setShowSavedModal ( false ) }
237- >
238- Close
239- </ Button >
366+ < DialogFooter className = "p-4 sm:p-6 border-t" >
367+ < div className = "flex flex-col sm:flex-row gap-2 sm:gap-4 w-full justify-center" >
368+ { savedMessages . length > 0 && (
369+ < Button
370+ variant = "outline"
371+ onClick = { handleShareAllMessages }
372+ className = "flex-1 sm:flex-none"
373+ >
374+ < Share2 className = "w-4 h-4 mr-2" />
375+ 모든 메시지 공유하기
376+ </ Button >
377+ ) }
378+ < Button
379+ variant = "secondary"
380+ onClick = { ( ) => setShowSavedModal ( false ) }
381+ className = "flex-1 sm:flex-none"
382+ >
383+ 닫기
384+ </ Button >
385+ </ div >
240386 </ DialogFooter >
241387 </ DialogContent >
242388 </ Dialog >
0 commit comments