1- import { lazy , Suspense , useEffect , useMemo , useState } from "react" ;
21import styles from "./project.module.css" ;
32
3+ import { lazy , Suspense , useEffect , useMemo , useState } from "react" ;
4+ import { LinkIcon , LockIcon } from "lucide-react" ;
5+ import { useLocalStorage } from "@uidotdev/usehooks" ;
6+ import * as Tabs from "@radix-ui/react-tabs" ;
7+
8+ import { resolveRange , type RangeName } from "../api/ranges" ;
49import { api , dimensionNames , formatMetricVal , metricNames , useQuery } from "../api" ;
510import type { DateRange , Dimension , DimensionTableRow , Metric , ProjectResponse , StatsResponse } from "../api" ;
611
7- import { useLocalStorage } from "@uidotdev/usehooks" ;
8- import { LiveVisitorCount , ProjectOverview , SelectRange } from "./projects" ;
9- import { resolveRange , type RangeName } from "../api/ranges" ;
1012import { BrowserIcon , MobileDeviceIcon , OSIcon , ReferrerIcon } from "./icons" ;
11- import { LinkIcon , LockIcon } from "lucide-react" ;
12- const server = typeof window === "undefined" ;
13+ import { LiveVisitorCount , ProjectOverview , SelectRange } from "./projects" ;
1314
1415const WorldMap = lazy ( ( ) => import ( "./worldmap" ) . then ( ( module ) => ( { default : module . WorldMap } ) ) ) ;
1516
@@ -19,7 +20,7 @@ export const Project = () => {
1920 const [ metric , setMetric ] = useLocalStorage < Metric > ( "metric" , "views" ) ;
2021
2122 useEffect ( ( ) => {
22- if ( server ) return ;
23+ if ( typeof window === "undefined" ) return ;
2324 setProjectId ( window ?. document . location . pathname . split ( "/" ) . pop ( ) ?? null ) ;
2425 } , [ ] ) ;
2526
@@ -43,33 +44,24 @@ export const Project = () => {
4344 renderHeader = { ( props ) => < ProjectHeader { ...props } range = { dateRange } setRange = { setDateRange } /> }
4445 />
4546 < div className = { styles . tables } >
46- < Card >
47- < DimTable project = { data } dimension = { "platform" } metric = { metric } range = { resolveRange ( dateRange ) . range } />
48- </ Card >
49-
50- < Card >
51- < DimTable project = { data } dimension = { "browser" } metric = { metric } range = { resolveRange ( dateRange ) . range } />
52- </ Card >
53- < Card fullWidth >
54- < GeoCard project = { data } metric = { metric } range = { resolveRange ( dateRange ) . range } />
55- </ Card >
5647 < Card >
5748 < DimTable project = { data } dimension = { "url" } metric = { metric } range = { resolveRange ( dateRange ) . range } />
5849 </ Card >
5950 < Card >
60- < DimTable project = { data } dimension = { "fqdn " } metric = { metric } range = { resolveRange ( dateRange ) . range } />
51+ < DimTable project = { data } dimension = { "referrer " } metric = { metric } range = { resolveRange ( dateRange ) . range } />
6152 </ Card >
53+ < GeoCard project = { data } metric = { metric } range = { resolveRange ( dateRange ) . range } />
6254 < Card >
63- < DimTable project = { data } dimension = { "mobile " } metric = { metric } range = { resolveRange ( dateRange ) . range } />
55+ < DimTable project = { data } dimension = { "platform " } metric = { metric } range = { resolveRange ( dateRange ) . range } />
6456 </ Card >
6557 < Card >
66- < DimTable project = { data } dimension = { "referrer " } metric = { metric } range = { resolveRange ( dateRange ) . range } />
58+ < DimTable project = { data } dimension = { "browser " } metric = { metric } range = { resolveRange ( dateRange ) . range } />
6759 </ Card >
6860 < Card >
69- < DimTable project = { data } dimension = { "city " } metric = { metric } range = { resolveRange ( dateRange ) . range } />
61+ < DimTable project = { data } dimension = { "fqdn " } metric = { metric } range = { resolveRange ( dateRange ) . range } />
7062 </ Card >
7163 < Card >
72- < DimTable project = { data } dimension = { "country " } metric = { metric } range = { resolveRange ( dateRange ) . range } />
64+ < DimTable project = { data } dimension = { "mobile " } metric = { metric } range = { resolveRange ( dateRange ) . range } />
7365 </ Card >
7466 </ div >
7567 </ div >
@@ -108,18 +100,6 @@ const ProjectHeader = ({
108100 ) ;
109101} ;
110102
111- const Entities = ( { entities } : { entities : { id : string ; displayName : string } [ ] } ) => {
112- return (
113- < div className = { styles . entities } >
114- { entities . map ( ( entity ) => (
115- < div key = { entity . id } className = { styles . entity } >
116- < h3 > { entity . displayName } </ h3 >
117- </ div >
118- ) ) }
119- </ div >
120- ) ;
121- } ;
122-
123103const Card = ( { children, fullWidth } : { children : React . ReactNode ; fullWidth ?: boolean } ) => {
124104 return (
125105 < div className = { styles . card } data-full-width = { fullWidth ?? undefined } >
@@ -149,26 +129,35 @@ const GeoCard = ({ project, metric, range }: { project: ProjectResponse; metric:
149129 const order = useMemo ( ( ) => data ?. data ?. sort ( ( a , b ) => b . value - a . value ) . map ( ( d ) => d . dimensionValue ) , [ data ] ) ;
150130
151131 return (
152- < div className = { styles . geoCard } >
153- < div >
154- < WorldMap data = { data ?. data } metric = { metric } />
155- </ div >
156- < div >
157- { data ?. data ?. map ( ( d ) => {
158- const value = metric === "avg_views_per_session" ? d . value / 1000 : d . value ;
159- const biggestVal = metric === "avg_views_per_session" ? biggest / 1000 : biggest ;
160-
161- return (
162- < div key = { d . dimensionValue } style = { { order : order ?. indexOf ( d . dimensionValue ) } } className = { styles . dimRow } >
163- < DimensionValueBar value = { value } biggest = { biggestVal } >
164- < DimensionLabel dimension = { "country" } value = { d } />
165- </ DimensionValueBar >
166-
167- < div > { value . toFixed ( 1 ) . replace ( / \. 0 $ / , "" ) || "0" } </ div >
168- </ div >
169- ) ;
170- } ) }
171- </ div >
132+ < div className = { `${ styles . card } ${ styles . geoCard } ` } data-full-width = "true" >
133+ < Suspense >
134+ < div >
135+ < div className = { styles . geoMap } >
136+ < WorldMap data = { data ?. data } metric = { metric } />
137+ </ div >
138+ < div className = { styles . geoTable } >
139+ < Tabs . Root className = { styles . tabs } defaultValue = "cities" >
140+ < Tabs . List className = { styles . tabsList } >
141+ < Tabs . Trigger value = "countries" > Countries</ Tabs . Trigger >
142+ < Tabs . Trigger value = "cities" > Cities</ Tabs . Trigger >
143+ < div > { metricNames [ metric ] } </ div >
144+ </ Tabs . List >
145+ < Tabs . Content value = "countries" >
146+ < DimList
147+ value = { data ?. data ?? [ ] }
148+ dimension = { "country" }
149+ metric = { metric }
150+ biggest = { biggest }
151+ order = { order }
152+ />
153+ </ Tabs . Content >
154+ < Tabs . Content value = "cities" >
155+ < DimTable project = { project } dimension = { "city" } metric = { metric } range = { range } noHeader />
156+ </ Tabs . Content >
157+ </ Tabs . Root >
158+ </ div >
159+ </ div >
160+ </ Suspense >
172161 </ div >
173162 ) ;
174163} ;
@@ -178,7 +167,8 @@ const DimTable = ({
178167 dimension,
179168 metric,
180169 range,
181- } : { project : ProjectResponse ; dimension : Dimension ; metric : Metric ; range : DateRange } ) => {
170+ noHeader,
171+ } : { project : ProjectResponse ; dimension : Dimension ; metric : Metric ; range : DateRange ; noHeader ?: boolean } ) => {
182172 const { data } = useQuery ( {
183173 placeholderData : ( prev ) => prev ,
184174 queryKey : [ "dimension" , project . id , dimension , metric , range ] ,
@@ -200,21 +190,40 @@ const DimTable = ({
200190
201191 return (
202192 < div className = { styles . dimTable } >
203- < div className = { styles . header } >
204- < div > { dimensionNames [ dimension ] } </ div >
205- < div > { metricNames [ metric ] } </ div >
206- </ div >
207- { data ?. data ?. map ( ( d ) => {
208- const value = d . value ;
209- const biggestVal = biggest ;
193+ { ! noHeader && (
194+ < div className = { styles . header } >
195+ < div > { dimensionNames [ dimension ] } </ div >
196+ < div > { metricNames [ metric ] } </ div >
197+ </ div >
198+ ) }
199+ < DimList value = { data ?. data ?? [ ] } dimension = { dimension } metric = { metric } biggest = { biggest } order = { order } />
200+ </ div >
201+ ) ;
202+ } ;
210203
204+ const DimList = ( {
205+ value,
206+ dimension,
207+ metric,
208+ biggest,
209+ order,
210+ } : {
211+ value : DimensionTableRow [ ] ;
212+ dimension : Dimension ;
213+ metric : Metric ;
214+ biggest : number ;
215+ order ?: string [ ] ;
216+ } ) => {
217+ return (
218+ < div >
219+ { value . map ( ( d ) => {
211220 return (
212221 < div key = { d . dimensionValue } style = { { order : order ?. indexOf ( d . dimensionValue ) } } className = { styles . dimRow } >
213- < DimensionValueBar value = { value } biggest = { biggestVal } >
222+ < DimensionValueBar value = { d . value } biggest = { biggest } >
214223 < DimensionLabel dimension = { dimension } value = { d } />
215224 </ DimensionValueBar >
216225
217- < div > { formatMetricVal ( metric , value ) } </ div >
226+ < div > { formatMetricVal ( metric , d . value ) } </ div >
218227 </ div >
219228 ) ;
220229 } ) }
0 commit comments