33import { useState , useEffect } from 'react' ;
44import Link from 'next/link' ;
55import { Search } from 'lucide-react' ;
6- import type { Post , Category , Series } from '@/types/archive' ;
6+ import type { Post , Category , Subject } from '@/types/archive' ;
77import { useLang } from '@/components/layout/LangProvider' ;
88import i18n from '@/lib/i18n' ;
99
1010interface BookArchiveProps {
1111 posts : Post [ ] ;
1212 categories : Category [ ] ;
13- series : Series [ ] ;
13+ subjects : Subject [ ] ;
1414 fontClassName : string ;
1515}
1616
@@ -48,50 +48,63 @@ function Ornament() {
4848 ) ;
4949}
5050
51- export default function BookArchive ( { posts, categories, series , fontClassName } : BookArchiveProps ) {
51+ export default function BookArchive ( { posts, categories, subjects , fontClassName } : BookArchiveProps ) {
5252 const [ selectedCategory , setSelectedCategory ] = useState ( 'all' ) ;
53- const [ selectedSeries , setSelectedSeries ] = useState ( '' ) ;
53+ const [ selectedSubject , setSelectedSubject ] = useState ( '' ) ;
5454 const [ mobilePage , setMobilePage ] = useState < 'posts' | 'index' > ( 'posts' ) ;
5555 const { lang } = useLang ( ) ;
5656 const t = i18n [ lang ] ;
5757
58- const SERIES_MAP : Record < string , string > = {
58+ const SUBJECT_MAP : Record < string , string > = {
5959 '프롬프트 가이드' : 'Prompt Guide' ,
6060 'SQL 완전 정복' : 'SQL Mastery' ,
6161 'GA4 완전 정복' : 'GA4 Mastery' ,
6262 '애자일 가이드' : 'Agile Guide' ,
6363 } ;
64- const SERIES_MAP_REVERSE = Object . fromEntries (
65- Object . entries ( SERIES_MAP ) . map ( ( [ k , v ] ) => [ v , k ] )
64+ const SUBJECT_MAP_REVERSE = Object . fromEntries (
65+ Object . entries ( SUBJECT_MAP ) . map ( ( [ k , v ] ) => [ v , k ] )
6666 ) ;
6767
6868 useEffect ( ( ) => {
69- if ( ! selectedSeries ) return ;
69+ if ( ! selectedSubject ) return ;
7070 if ( lang === 'en' ) {
71- const mapped = SERIES_MAP [ selectedSeries ] ;
72- if ( mapped ) setSelectedSeries ( mapped ) ;
73- else setSelectedSeries ( '' ) ;
71+ const mapped = SUBJECT_MAP [ selectedSubject ] ;
72+ if ( mapped ) setSelectedSubject ( mapped ) ;
73+ else setSelectedSubject ( '' ) ;
7474 } else {
75- const mapped = SERIES_MAP_REVERSE [ selectedSeries ] ;
76- if ( mapped ) setSelectedSeries ( mapped ) ;
77- else setSelectedSeries ( '' ) ;
75+ const mapped = SUBJECT_MAP_REVERSE [ selectedSubject ] ;
76+ if ( mapped ) setSelectedSubject ( mapped ) ;
77+ else setSelectedSubject ( '' ) ;
7878 }
7979 // eslint-disable-next-line react-hooks/exhaustive-deps
8080 } , [ lang ] ) ;
8181
8282 const filtered = posts
8383 . filter ( ( p ) => {
8484 const catMatch = selectedCategory === 'all' || p . category === selectedCategory ;
85- const seriesMatch = ! selectedSeries || p . series === selectedSeries ;
86- return catMatch && seriesMatch ;
85+ const subjectMatch = ! selectedSubject || p . subject === selectedSubject ;
86+ return catMatch && subjectMatch ;
8787 } )
8888 . sort ( ( a , b ) => {
89- if ( selectedSeries ) return a . date < b . date ? - 1 : 1 ;
89+ if ( selectedSubject ) return a . date < b . date ? - 1 : 1 ;
9090 return a . date > b . date ? - 1 : 1 ;
9191 } ) ;
9292
93- const isActive = ( catId : string , seriesId = '' ) =>
94- seriesId ? selectedSeries === seriesId : selectedCategory === catId && ! selectedSeries ;
93+ const isActive = ( catId : string , subjectId = '' ) =>
94+ subjectId ? selectedSubject === subjectId : selectedCategory === catId && ! selectedSubject ;
95+
96+ const visibleSubjects = selectedCategory === 'all'
97+ ? subjects
98+ : Array . from (
99+ posts
100+ . filter ( ( post ) => post . category === selectedCategory && post . subject )
101+ . reduce ( ( countMap , post ) => {
102+ const subject = post . subject ;
103+ if ( ! subject ) return countMap ;
104+ countMap . set ( subject , ( countMap . get ( subject ) ?? 0 ) + 1 ) ;
105+ return countMap ;
106+ } , new Map < string , number > ( ) ) ,
107+ ) . map ( ( [ id , count ] ) => ( { id, label : id , count } ) ) ;
95108
96109 const BOOK_HEIGHT = '74vh' ;
97110
@@ -147,7 +160,7 @@ export default function BookArchive({ posts, categories, series, fontClassName }
147160 { [ { id : 'all' , label : t . allPosts , count : posts . length } , ...categories . filter ( c => c . id !== 'all' ) ] . map ( ( cat ) => (
148161 < button
149162 key = { cat . id }
150- onClick = { ( ) => { setSelectedCategory ( cat . id ) ; setSelectedSeries ( '' ) ; setMobilePage ( 'posts' ) ; } }
163+ onClick = { ( ) => { setSelectedCategory ( cat . id ) ; setSelectedSubject ( '' ) ; setMobilePage ( 'posts' ) ; } }
151164 className = "text-left flex items-center justify-between py-2 text-sm transition-all"
152165 style = { { color : isActive ( cat . id ) ? TEXT_ACTIVE : TEXT_INACTIVE } }
153166 >
@@ -160,15 +173,15 @@ export default function BookArchive({ posts, categories, series, fontClassName }
160173 ) ) }
161174 </ div >
162175 { /* 시리즈 */ }
163- { series . length > 0 && (
176+ { visibleSubjects . length > 0 && (
164177 < >
165178 < Ornament />
166179 < div className = "flex flex-col mt-1 gap-0.5" >
167- < p className = "text-xs tracking-[0.3em] uppercase mb-2" style = { { color : TEXT_LABEL } } > { t . seriesLabel } </ p >
168- { series . map ( ( s ) => (
180+ < p className = "text-xs tracking-[0.3em] uppercase mb-2" style = { { color : TEXT_LABEL } } > { t . subjectLabel } </ p >
181+ { visibleSubjects . map ( ( s ) => (
169182 < button
170183 key = { s . id }
171- onClick = { ( ) => { setSelectedSeries ( s . id ) ; setSelectedCategory ( 'all' ) ; setMobilePage ( 'posts' ) ; } }
184+ onClick = { ( ) => { setSelectedSubject ( s . id ) ; setMobilePage ( 'posts' ) ; } }
172185 className = "text-left flex items-center justify-between py-2 text-sm transition-all"
173186 style = { { color : isActive ( '' , s . id ) ? TEXT_ACTIVE : TEXT_INACTIVE } }
174187 >
@@ -192,7 +205,7 @@ export default function BookArchive({ posts, categories, series, fontClassName }
192205 < RulingLines />
193206 < div className = "mt-4 mb-4" >
194207 < p className = "text-xs tracking-[0.35em] uppercase" style = { { color : TEXT_LABEL } } >
195- { selectedSeries || ( selectedCategory === 'all' ? t . allPosts : selectedCategory ) }
208+ { selectedSubject || ( selectedCategory === 'all' ? t . allPosts : selectedCategory ) }
196209 </ p >
197210 </ div >
198211 < div className = "flex flex-col gap-4" >
@@ -284,7 +297,7 @@ export default function BookArchive({ posts, categories, series, fontClassName }
284297 { [ { id : 'all' , label : t . allPosts , count : posts . length } , ...categories . filter ( c => c . id !== 'all' ) ] . map ( ( cat ) => (
285298 < button
286299 key = { cat . id }
287- onClick = { ( ) => { setSelectedCategory ( cat . id ) ; setSelectedSeries ( '' ) ; } }
300+ onClick = { ( ) => { setSelectedCategory ( cat . id ) ; setSelectedSubject ( '' ) ; } }
288301 className = "group text-left flex items-center justify-between py-2 text-sm transition-all"
289302 style = { { color : isActive ( cat . id ) ? TEXT_ACTIVE : TEXT_INACTIVE } }
290303 >
@@ -301,17 +314,17 @@ export default function BookArchive({ posts, categories, series, fontClassName }
301314 </ div >
302315
303316 { /* 시리즈 */ }
304- { series . length > 0 && (
317+ { visibleSubjects . length > 0 && (
305318 < >
306319 < Ornament />
307320 < div className = "flex flex-col mt-1 gap-0.5" >
308321 < p className = "text-xs tracking-[0.3em] uppercase mb-2" style = { { color : TEXT_LABEL } } >
309- { t . seriesLabel }
322+ { t . subjectLabel }
310323 </ p >
311- { series . map ( ( s ) => (
324+ { visibleSubjects . map ( ( s ) => (
312325 < button
313326 key = { s . id }
314- onClick = { ( ) => { setSelectedSeries ( s . id ) ; setSelectedCategory ( 'all' ) ; } }
327+ onClick = { ( ) => { setSelectedSubject ( s . id ) ; } }
315328 className = "text-left flex items-center justify-between py-2 text-sm transition-all"
316329 style = { { color : isActive ( '' , s . id ) ? TEXT_ACTIVE : TEXT_INACTIVE } }
317330 >
@@ -364,7 +377,7 @@ export default function BookArchive({ posts, categories, series, fontClassName }
364377
365378 < div className = "mt-6 mb-5" >
366379 < p className = "text-[9px] tracking-[0.35em] uppercase" style = { { color : TEXT_LABEL } } >
367- { selectedSeries || ( selectedCategory === 'all' ? t . allPosts : selectedCategory ) }
380+ { selectedSubject || ( selectedCategory === 'all' ? t . allPosts : selectedCategory ) }
368381 </ p >
369382 </ div >
370383
0 commit comments