1- import React , { FC , Fragment , useCallback } from 'react' ;
2- import { useModals } from '@gitroom/frontend/components/layout/new-modal' ;
1+ import React , { FC , Fragment , useCallback , useMemo , useState } from 'react' ;
32import useSWR from 'swr' ;
43import { useFetch } from '@gitroom/helpers/utils/custom.fetch' ;
54import { useT } from '@gitroom/react/translation/get.transation.service.client' ;
5+ import { ChartSocial } from '@gitroom/frontend/components/analytics/chart-social' ;
6+ import { Select } from '@gitroom/react/form/select' ;
7+ import { LoadingComponent } from '@gitroom/frontend/components/layout/loading' ;
8+
9+ interface AnalyticsData {
10+ label : string ;
11+ data : Array < { total : number ; date : string } > ;
12+ percentageChange : number ;
13+ average ?: boolean ;
14+ }
15+
616export const StatisticsModal : FC < {
717 postId : string ;
818} > = ( props ) => {
919 const { postId } = props ;
10- const modals = useModals ( ) ;
1120 const t = useT ( ) ;
1221 const fetch = useFetch ( ) ;
22+ const [ dateRange , setDateRange ] = useState ( 7 ) ;
23+
1324 const loadStatistics = useCallback ( async ( ) => {
1425 return ( await fetch ( `/posts/${ postId } /statistics` ) ) . json ( ) ;
15- } , [ postId ] ) ;
16- const closeAll = useCallback ( ( ) => {
17- modals . closeAll ( ) ;
18- } , [ ] ) ;
19- const { data, isLoading } = useSWR (
26+ } , [ postId , fetch ] ) ;
27+
28+ const loadPostAnalytics = useCallback ( async ( ) => {
29+ return ( await fetch ( `/analytics/post/${ postId } ?date=${ dateRange } ` ) ) . json ( ) ;
30+ } , [ postId , dateRange , fetch ] ) ;
31+
32+ const { data : statisticsData , isLoading : isLoadingStatistics } = useSWR (
2033 `/posts/${ postId } /statistics` ,
2134 loadStatistics
2235 ) ;
36+
37+ const { data : analyticsData , isLoading : isLoadingAnalytics } = useSWR (
38+ `/analytics/post/${ postId } ?date=${ dateRange } ` ,
39+ loadPostAnalytics ,
40+ {
41+ revalidateOnFocus : false ,
42+ revalidateOnReconnect : false ,
43+ revalidateIfStale : false ,
44+ revalidateOnMount : true ,
45+ refreshWhenHidden : false ,
46+ refreshWhenOffline : false ,
47+ }
48+ ) ;
49+
50+ const dateOptions = useMemo ( ( ) => {
51+ return [
52+ { key : 7 , value : t ( '7_days' , '7 Days' ) } ,
53+ { key : 30 , value : t ( '30_days' , '30 Days' ) } ,
54+ { key : 90 , value : t ( '90_days' , '90 Days' ) } ,
55+ ] ;
56+ } , [ t ] ) ;
57+
58+ const totals = useMemo ( ( ) => {
59+ if ( ! analyticsData || ! Array . isArray ( analyticsData ) ) return [ ] ;
60+ return analyticsData . map ( ( p : AnalyticsData ) => {
61+ const value =
62+ ( p ?. data ?. reduce ( ( acc : number , curr : any ) => acc + Number ( curr . total ) , 0 ) || 0 ) /
63+ ( p . average ? p . data . length : 1 ) ;
64+ if ( p . average ) {
65+ return value . toFixed ( 2 ) + '%' ;
66+ }
67+ return Math . round ( value ) ;
68+ } ) ;
69+ } , [ analyticsData ] ) ;
70+
71+ const isLoading = isLoadingStatistics || isLoadingAnalytics ;
72+
2373 return (
24- < div className = "relative" >
74+ < div className = "relative min-h-[200px] " >
2575 { isLoading ? (
26- < div > { t ( 'loading' , 'Loading' ) } </ div >
76+ < div className = "flex items-center justify-center py-[40px]" >
77+ < LoadingComponent />
78+ </ div >
2779 ) : (
28- < >
29- { data . clicks . length === 0 ? (
30- 'No Results'
31- ) : (
32- < >
33- < div className = "grid grid-cols-3 mt-[20px]" >
80+ < div className = "flex flex-col gap-[24px]" >
81+ { /* Post Analytics Section */ }
82+ { analyticsData && Array . isArray ( analyticsData ) && analyticsData . length > 0 && (
83+ < div className = "flex flex-col gap-[14px]" >
84+ < div className = "flex items-center justify-between" >
85+ < h3 className = "text-[18px] font-[500]" >
86+ { t ( 'post_analytics' , 'Post Analytics' ) }
87+ </ h3 >
88+ < div className = "max-w-[150px]" >
89+ < Select
90+ label = ""
91+ name = "date"
92+ disableForm = { true }
93+ hideErrors = { true }
94+ value = { dateRange }
95+ onChange = { ( e ) => setDateRange ( + e . target . value ) }
96+ >
97+ { dateOptions . map ( ( option ) => (
98+ < option key = { option . key } value = { option . key } >
99+ { option . value }
100+ </ option >
101+ ) ) }
102+ </ Select >
103+ </ div >
104+ </ div >
105+ < div className = "grid grid-cols-3 gap-[20px]" >
106+ { analyticsData . map ( ( p : AnalyticsData , index : number ) => (
107+ < div key = { `analytics-${ index } ` } className = "flex" >
108+ < div className = "flex-1 bg-newTableHeader rounded-[8px] py-[10px] px-[16px] gap-[10px] flex flex-col" >
109+ < div className = "flex items-center gap-[14px]" >
110+ < div className = "text-[20px]" > { p . label } </ div >
111+ </ div >
112+ < div className = "flex-1" >
113+ < div className = "h-[156px] relative" >
114+ < ChartSocial data = { p . data } key = { `chart-${ index } ` } />
115+ </ div >
116+ </ div >
117+ < div className = "text-[50px] leading-[60px]" > { totals [ index ] } </ div >
118+ </ div >
119+ </ div >
120+ ) ) }
121+ </ div >
122+ </ div >
123+ ) }
124+
125+ { /* Short Links Statistics Section */ }
126+ < div className = "flex flex-col gap-[14px]" >
127+ < h3 className = "text-[18px] font-[500]" >
128+ { t ( 'short_links_statistics' , 'Short Links Statistics' ) }
129+ </ h3 >
130+ { statisticsData ?. clicks ?. length === 0 ? (
131+ < div className = "text-gray-400" >
132+ { t ( 'no_short_link_results' , 'No short link results' ) }
133+ </ div >
134+ ) : (
135+ < div className = "grid grid-cols-3" >
34136 < div className = "bg-forth p-[4px] rounded-tl-lg" >
35137 { t ( 'short_link' , 'Short Link' ) }
36138 </ div >
@@ -40,7 +142,7 @@ export const StatisticsModal: FC<{
40142 < div className = "bg-forth p-[4px] rounded-tr-lg" >
41143 { t ( 'clicks' , 'Clicks' ) }
42144 </ div >
43- { data . clicks . map ( ( p : any ) => (
145+ { statisticsData ? .clicks ? .map ( ( p : any ) => (
44146 < Fragment key = { p . short } >
45147 < div className = "p-[4px] py-[10px] bg-customColor6" >
46148 { p . short }
@@ -54,9 +156,17 @@ export const StatisticsModal: FC<{
54156 </ Fragment >
55157 ) ) }
56158 </ div >
57- </ >
58- ) }
59- </ >
159+ ) }
160+ </ div >
161+
162+ { /* No analytics available message */ }
163+ { ( ! analyticsData || ! Array . isArray ( analyticsData ) || analyticsData . length === 0 ) &&
164+ ( ! statisticsData ?. clicks || statisticsData . clicks . length === 0 ) && (
165+ < div className = "text-center text-gray-400 py-[20px]" >
166+ { t ( 'no_statistics_available' , 'No statistics available for this post' ) }
167+ </ div >
168+ ) }
169+ </ div >
60170 ) }
61171 </ div >
62172 ) ;
0 commit comments