11'use client' ;
22
33import { Line } from 'react-chartjs-2' ;
4-
54import {
65 Chart as ChartJS ,
76 CategoryScale ,
@@ -13,12 +12,12 @@ import {
1312 Legend ,
1413} from 'chart.js' ;
1514import { useQuery } from '@tanstack/react-query' ;
16- import { useState } from 'react' ;
15+ import { useEffect , useState } from 'react' ;
1716import { COLORS , PATHS , SCREENS } from '@/constants' ;
1817import { Dropdown , Input } from '@/components' ;
18+ import { PostDetailValue } from '@/types' ;
1919import { useResponsive } from '@/hooks' ;
2020import { postDetail } from '@/apis' ;
21- import { PostDetailValue } from '@/types' ;
2221
2322ChartJS . register (
2423 CategoryScale ,
@@ -45,60 +44,94 @@ interface IProp {
4544 releasedAt : string ;
4645}
4746
47+ type ModeType = 'none' | 'weekly' | 'monthly' | 'custom' ;
48+
4849export const Graph = ( { id, releasedAt } : IProp ) => {
4950 const width = useResponsive ( ) ;
5051
5152 const isMBI = width < SCREENS . MBI ;
5253
53- const [ type , setType ] = useState ( {
54- start : '' ,
55- end : '' ,
56- type : 'View' ,
57- } ) ;
54+ const [ type , setType ] = useState ( { start : '' , end : '' , type : 'View' } ) ;
55+ const [ mode , setMode ] = useState < ModeType > ( 'none' ) ;
5856
5957 const { data : datas } = useQuery ( {
6058 queryKey : [ PATHS . DETAIL , type ] ,
6159 queryFn : async ( ) => await postDetail ( id , type . start , type . end ) ,
62- select : ( { post } ) => ( {
63- labels : post . map ( ( i ) => i . date . split ( 'T' ) [ 0 ] ) ,
64- datasets : [
65- {
66- label : type . type ,
67- data : post . map (
68- ( i ) => i [ `daily${ type . type } Count` as keyof PostDetailValue ] ,
69- ) ,
70- ...datasets ,
71- } ,
72- ] ,
73- } ) ,
60+ select : ( { post } ) => {
61+ post = post . sort (
62+ ( a , b ) => new Date ( a . date ) . getTime ( ) - new Date ( b . date ) . getTime ( ) ,
63+ ) ;
64+ return {
65+ labels : post . map ( ( i ) => i . date . split ( 'T' ) [ 0 ] ) ,
66+ datasets : [
67+ {
68+ label : type . type ,
69+ data : post . map (
70+ ( i ) => i [ `daily${ type . type } Count` as keyof PostDetailValue ] ,
71+ ) ,
72+ ...datasets ,
73+ } ,
74+ ] ,
75+ } ;
76+ } ,
7477 enabled : ! ! type . start && ! ! type . end ,
7578 } ) ;
7679
80+ useEffect ( ( ) => {
81+ if ( mode === 'none' || mode === 'custom' ) {
82+ setType ( ( prev ) => ( { ...prev , start : '' , end : '' } ) ) ;
83+ } else {
84+ const start = new Date ( ) ;
85+ if ( mode === 'monthly' ) start . setMonth ( start . getMonth ( ) - 1 ) ;
86+ else start . setDate ( start . getDate ( ) - 7 ) ;
87+
88+ setType ( ( prev ) => ( {
89+ ...prev ,
90+ start : start . toISOString ( ) . split ( 'T' ) [ 0 ] ,
91+ end : new Date ( ) . toISOString ( ) . split ( 'T' ) [ 0 ] ,
92+ } ) ) ;
93+ }
94+ } , [ mode ] ) ;
95+
7796 return (
7897 < div className = "w-full bg-BG-SUB flex flex-col items-center px-[25px] pb-[30px] gap-[30px] max-MBI:px-5 max-MBI:pb-10" >
7998 < div className = "flex items-center gap-[20px] flex-wrap justify-center max-TBL:gap-[10px]" >
80- < Input
81- size = { isMBI ? 'SMALL' : 'MEDIUM' }
82- form = "SMALL"
83- value = { type . start }
84- min = { releasedAt . split ( 'T' ) [ 0 ] }
85- onChange = { ( e ) => setType ( { ...type , start : e . target . value } ) }
86- placeholder = "시작 날짜"
87- type = "date"
88- />
89- < span className = "text-ST4 max-TBL:text-T5 text-TEXT-MAIN" > ~</ span >
90- < Input
91- size = { isMBI ? 'SMALL' : 'MEDIUM' }
92- form = "SMALL"
93- value = { type . end }
94- min = { type . start ? type . start : releasedAt . split ( 'T' ) [ 0 ] }
95- onChange = { ( e ) => setType ( { ...type , end : e . target . value } ) }
96- placeholder = "종료 날짜"
97- type = "date"
99+ { mode === 'custom' && (
100+ < >
101+ < Input
102+ size = { isMBI ? 'SMALL' : 'MEDIUM' }
103+ form = "SMALL"
104+ value = { type . start }
105+ min = { releasedAt . split ( 'T' ) [ 0 ] }
106+ onChange = { ( e ) => setType ( { ...type , start : e . target . value } ) }
107+ placeholder = "시작 날짜"
108+ type = "date"
109+ />
110+ < span className = "text-ST4 max-TBL:text-T5 text-TEXT-MAIN" > ~</ span >
111+ < Input
112+ size = { isMBI ? 'SMALL' : 'MEDIUM' }
113+ form = "SMALL"
114+ value = { type . end }
115+ min = { type . start ? type . start : releasedAt . split ( 'T' ) [ 0 ] }
116+ onChange = { ( e ) => setType ( { ...type , end : e . target . value } ) }
117+ placeholder = "종료 날짜"
118+ type = "date"
119+ />
120+ </ >
121+ ) }
122+ < Dropdown
123+ onChange = { ( e ) => setMode ( e as ModeType ) }
124+ defaultValue = "미선택"
125+ options = { [
126+ [ '미선택' , 'none' ] ,
127+ [ '지난 7일' , 'weekly' ] ,
128+ [ '지난 30일' , 'monthly' ] ,
129+ [ '직접선택' , 'custom' ] ,
130+ ] }
98131 />
99132 < Dropdown
100133 onChange = { ( e ) => setType ( { ...type , type : e as string } ) }
101- defaultValue = { ' 조회수' }
134+ defaultValue = " 조회수"
102135 options = { [
103136 [ '조회수' , 'View' ] ,
104137 [ '좋아요' , 'Like' ] ,
@@ -120,24 +153,10 @@ export const Graph = ({ id, releasedAt }: IProp) => {
120153 maintainAspectRatio : false ,
121154 animation : false ,
122155 interaction : { mode : 'nearest' , intersect : false } ,
123- plugins : {
124- legend : {
125- display : false ,
126- } ,
127- } ,
156+ plugins : { legend : { display : false } } ,
128157 scales : {
129- x : {
130- axis : 'x' ,
131- grid : {
132- color : COLORS . BORDER . SUB ,
133- } ,
134- } ,
135- y : {
136- axis : 'y' ,
137- grid : {
138- color : COLORS . BORDER . SUB ,
139- } ,
140- } ,
158+ x : { axis : 'x' , grid : { color : COLORS . BORDER . SUB } } ,
159+ y : { axis : 'y' , grid : { color : COLORS . BORDER . SUB } } ,
141160 } ,
142161 } }
143162 className = "w-[100%_!important] h-[auto_!important] max-h-[300px]"
0 commit comments