22
33import { Suspense } from "@suspensive/react" ;
44import { useSuspenseQuery } from "@tanstack/react-query" ;
5- import { chunk , noop } from "es-toolkit" ;
5+ import { chunk } from "es-toolkit" ;
66import Image from "next/image" ;
77import Link from "next/link" ;
88import { type HTMLAttributes , useState } from "react" ;
99import { Separated } from "react-simplikit" ;
1010
11- import ChevronLeft from "@/assets/chevron-left.svg" ;
12- import ChevronRight from "@/assets/chevron-right.svg" ;
11+ import LogoWordMark from "@/assets/logo-wordmark.svg" ;
12+ import ResetIcon from "@/assets/reset-20.svg" ;
13+ import { Button } from "@/components/ui/Button" ;
1314import { Skeleton } from "@/components/ui/Skeleton" ;
1415import { HStack , VStack } from "@/components/ui/Stack" ;
1516import { Text } from "@/components/ui/Text" ;
17+ import { TextButton } from "@/components/ui/TextButton" ;
18+ import { colors } from "@/styles" ;
1619
1720import { cheerQueryOptions } from "../../_api/cheer" ;
1821import * as styles from "./RecentCheers.css" ;
@@ -21,53 +24,68 @@ const BACKGROUND_COLORS = ["#FEF8DD", "#FDE5E3", "#E0F2FF"];
2124
2225export const RecentCheers = ( ) => {
2326 return (
24- < VStack gap = { 16 } >
25- < Text as = 'h2' color = 'text.normal' typo = 'title2Sb' >
26- 가게에 전하는 따뜻한 응원
27- </ Text >
28-
29- < Suspense clientOnly fallback = { < RecentSupportCardContentSkeleton /> } >
30- < RecentSupportCardContent />
31- </ Suspense >
32- </ VStack >
27+ < Suspense clientOnly fallback = { < RecentSupportCardContentSkeleton /> } >
28+ < RecentSupportCardContent />
29+ </ Suspense >
3330 ) ;
3431} ;
3532
3633const RecentSupportCardContent = ( ) => {
3734 const [ currentIndex , setCurrentIndex ] = useState ( 0 ) ;
3835
39- const { data } = useSuspenseQuery ( cheerQueryOptions ( 10 ) ) ;
36+ const { data } = useSuspenseQuery ( cheerQueryOptions ( 15 ) ) ;
4037
4138 const chunkedList = chunk ( data . cheers , 3 ) ;
4239
40+ const handleRefresh = ( ) => {
41+ setCurrentIndex ( ( currentIndex + 1 ) % chunkedList . length ) ;
42+ } ;
43+
4344 return (
44- < VStack gap = { 24 } >
45- < VStack >
46- { chunkedList [ currentIndex ] ?. map ( ( cheer , index ) => (
47- < Link key = { cheer . cheerId } href = { `/stores/${ cheer . storeId } ` } >
48- < RecentSupportCard
49- style = { {
50- backgroundColor : BACKGROUND_COLORS [ index % 3 ] ,
51- transform : index === 1 ? "rotate(-4deg)" : "translate3d(0,0,0)" ,
52- } }
53- store = { {
54- name : cheer . storeName ,
55- imageUrl : cheer . imageUrl ,
56- location : `${ cheer . storeDistrict } ${ cheer . storeNeighborhood } ` ,
57- category : cheer . storeCategory ,
58- } }
59- content = { cheer . cheerDescription }
60- />
61- </ Link >
62- ) ) }
45+ < VStack gap = { 16 } >
46+ < HStack justify = 'between' >
47+ < Text as = 'h2' color = 'text.normal' typo = 'title2Sb' >
48+ 가게에 전하는 따뜻한 응원
49+ </ Text >
50+ < TextButton variant = 'assistive' size = 'small' onClick = { handleRefresh } >
51+ < HStack gap = { 4 } align = 'center' >
52+ 새로고침
53+ < ResetIcon width = { 16 } height = { 16 } />
54+ </ HStack >
55+ </ TextButton >
56+ </ HStack >
57+ < VStack gap = { 20 } >
58+ < VStack >
59+ { chunkedList [ currentIndex ] ?. map ( ( cheer , index ) => (
60+ < Link key = { cheer . cheerId } href = { `/stores/${ cheer . storeId } ` } >
61+ < RecentSupportCard
62+ style = { {
63+ backgroundColor : BACKGROUND_COLORS [ index % 3 ] ,
64+ transform :
65+ index === 1 ? "rotate(-4deg)" : "translate3d(0,0,0)" ,
66+ } }
67+ store = { {
68+ name : cheer . storeName ,
69+ imageUrl : cheer . imageUrl ,
70+ location : `${ cheer . storeDistrict } ${ cheer . storeNeighborhood } ` ,
71+ category : cheer . storeCategory ,
72+ } }
73+ content = { cheer . cheerDescription }
74+ />
75+ </ Link >
76+ ) ) }
77+ </ VStack >
78+ < Link href = '/stores' >
79+ < Button
80+ variant = 'custom'
81+ size = 'large'
82+ className = { styles . showAllButton }
83+ fullWidth
84+ >
85+ 가게 전체보기
86+ </ Button >
87+ </ Link >
6388 </ VStack >
64-
65- < RecentSupportButtons
66- currentIndex = { currentIndex }
67- totalCount = { chunkedList . length - 1 }
68- onPrevious = { ( ) => setCurrentIndex ( currentIndex - 1 ) }
69- onNext = { ( ) => setCurrentIndex ( currentIndex + 1 ) }
70- />
7189 </ VStack >
7290 ) ;
7391} ;
@@ -90,15 +108,26 @@ const RecentSupportCard = ({
90108 return (
91109 < VStack gap = { 8 } className = { styles . recentSupportCard } { ...restProps } >
92110 < HStack gap = { 12 } >
93- < Image
94- width = { 40 }
95- height = { 40 }
96- alt = { `${ store . name } 가게 이미지` }
97- className = { styles . storeImage }
98- src = { store . imageUrl }
99- // TODO: 추후 제거
100- unoptimized
101- />
111+ { store . imageUrl ? (
112+ < Image
113+ width = { 40 }
114+ height = { 40 }
115+ alt = { `${ store . name } 가게 이미지` }
116+ className = { styles . storeImage }
117+ objectFit = 'cover'
118+ src = { store . imageUrl }
119+ // TODO: 추후 제거
120+ unoptimized
121+ />
122+ ) : (
123+ < span className = { styles . storeImageFallback } >
124+ < LogoWordMark
125+ width = { 30.16 }
126+ height = { 16 }
127+ color = { colors . coolNeutral [ 96 ] }
128+ />
129+ </ span >
130+ ) }
102131 < VStack gap = { 2 } >
103132 < Text as = 'span' typo = 'body2Sb' color = 'text.normal' >
104133 { store . name }
@@ -127,47 +156,34 @@ const RecentSupportCard = ({
127156 ) ;
128157} ;
129158
130- const RecentSupportButtons = ( {
131- currentIndex,
132- totalCount,
133- onPrevious,
134- onNext,
135- } : {
136- currentIndex : number ;
137- totalCount : number ;
138- onPrevious : ( ) => void ;
139- onNext : ( ) => void ;
140- } ) => {
141- return (
142- < HStack justify = 'between' >
143- < button
144- className = { styles . recentCheersButton }
145- disabled = { currentIndex === 0 }
146- onClick = { onPrevious }
147- >
148- < ChevronLeft width = { 24 } height = { 24 } />
149- </ button >
150- < button
151- className = { styles . recentCheersButton }
152- disabled = { currentIndex === totalCount }
153- onClick = { onNext }
154- >
155- < ChevronRight width = { 24 } height = { 24 } />
156- </ button >
157- </ HStack >
158- ) ;
159- } ;
160-
161159const RecentSupportCardContentSkeleton = ( ) => {
162160 return (
163- < VStack gap = { 24 } >
164- < Skeleton width = '100%' height = { 400 } radius = { 26 } />
165- < RecentSupportButtons
166- currentIndex = { 0 }
167- totalCount = { 0 }
168- onPrevious = { noop }
169- onNext = { noop }
170- />
161+ < VStack gap = { 16 } >
162+ < HStack justify = 'between' >
163+ < Text as = 'h2' color = 'text.normal' typo = 'title2Sb' >
164+ 가게에 전하는 따뜻한 응원
165+ </ Text >
166+ < TextButton variant = 'assistive' size = 'small' disabled >
167+ < HStack gap = { 4 } align = 'center' >
168+ 새로고침
169+ < ResetIcon width = { 16 } height = { 16 } />
170+ </ HStack >
171+ </ TextButton >
172+ </ HStack >
173+
174+ < VStack gap = { 20 } >
175+ < Skeleton width = '100%' height = { 400 } radius = { 26 } />
176+ < Link href = '/stores' >
177+ < Button
178+ variant = 'custom'
179+ size = 'large'
180+ className = { styles . showAllButton }
181+ fullWidth
182+ >
183+ 가게 전체보기
184+ </ Button >
185+ </ Link >
186+ </ VStack >
171187 </ VStack >
172188 ) ;
173189} ;
0 commit comments