1
- import React from "react"
2
1
import { kebabCase } from "lodash"
3
2
import { MdInfoOutline } from "react-icons/md"
4
3
import { Area , AreaChart , ResponsiveContainer , XAxis , YAxis } from "recharts"
5
- import { Box , Flex , Icon , Text , VStack } from "@chakra-ui/react"
4
+ import { Box , Flex , Icon , Text , VStack } from "@chakra-ui/react"
5
+
6
+ import type { StatsBoxMetric } from "@/lib/types"
7
+
8
+ import { RANGES } from "@/lib/constants"
6
9
7
- import { Direction } from "../../types"
8
10
import InlineLink from "../Link"
9
11
import OldText from "../OldText"
10
12
import StatErrorMessage from "../StatErrorMessage"
11
- import StatLoadingMessage from "../StatLoadingMessage"
12
13
import Tooltip from "../Tooltip"
13
14
import Translation from "../Translation"
14
15
15
- import { Metric , ranges } from "./useStatsBoxGrid"
16
-
17
- const tooltipContent = ( metric : Metric ) => (
16
+ const tooltipContent = ( metric : StatsBoxMetric ) => (
18
17
< div >
19
18
< Translation id = "data-provided-by" /> { " " }
20
19
< InlineLink to = { metric . apiUrl } > { metric . apiProvider } </ InlineLink >
21
20
</ div >
22
21
)
23
22
24
- interface IGridItemProps {
25
- metric : Metric
26
- dir ?: Direction
23
+ type GridItemProps = {
24
+ metric : StatsBoxMetric
27
25
}
28
26
29
- export const GridItem : React . FC < IGridItemProps > = ( { metric, dir } ) => {
27
+ export const GridItem = ( { metric } : GridItemProps ) => {
30
28
const { title, description, state, buttonContainer, range } = metric
31
- const isLoading = ! state . value
32
- const value = state . hasError ? (
29
+ const hasError = "error" in state
30
+ const hasData = "data" in state
31
+
32
+ const value = hasError ? (
33
33
< StatErrorMessage />
34
- ) : isLoading ? (
35
- < StatLoadingMessage />
36
34
) : (
37
35
< VStack >
38
36
< Box >
@@ -46,7 +44,7 @@ export const GridItem: React.FC<IGridItemProps> = ({ metric, dir }) => {
46
44
_hover = { { fill : "primary.base" } }
47
45
_active = { { fill : "primary.base" } }
48
46
_focus = { { fill : "primary.base" } }
49
- > </ Icon >
47
+ / >
50
48
</ Tooltip >
51
49
</ Box >
52
50
</ VStack >
@@ -55,29 +53,30 @@ export const GridItem: React.FC<IGridItemProps> = ({ metric, dir }) => {
55
53
// Returns either 90 or 30-day data range depending on `range` selection
56
54
const filteredData = ( data : Array < { timestamp : number } > ) => {
57
55
if ( ! data ) return
58
- if ( range === ranges [ 1 ] ) return [ ...data ]
56
+ if ( range === RANGES [ 1 ] ) return [ ...data ]
59
57
return data . filter ( ( { timestamp } ) => {
60
58
const millisecondRange = 1000 * 60 * 60 * 24 * 30
61
59
const now = new Date ( ) . getTime ( )
62
60
return timestamp >= now - millisecondRange
63
61
} )
64
62
}
65
63
66
- const minValue = state . data . reduce (
67
- ( prev , { value } ) => ( prev < value ? prev : value ) ,
68
- 1e42
69
- )
64
+ const minValue = hasData
65
+ ? state . data . reduce (
66
+ ( prev , { value } ) => ( prev < value ? prev : value ) ,
67
+ Infinity
68
+ )
69
+ : 0
70
70
71
- const maxValue = state . data . reduce (
72
- ( prev , { value } ) => ( prev > value ? prev : value ) ,
73
- 0
74
- )
71
+ const maxValue = hasData
72
+ ? state . data . reduce ( ( prev , { value } ) => ( prev > value ? prev : value ) , 0 )
73
+ : 0
75
74
76
75
const chart : React . ReactNode = (
77
76
< ResponsiveContainer width = "100%" height = "100%" >
78
77
< AreaChart
79
- data = { filteredData ( state . data ) }
80
- margin = { { insetInlineStart : - 5 , insetInlineEnd : - 5 } }
78
+ data = { hasData ? filteredData ( state . data ) : [ ] }
79
+ margin = { { left : - 5 , right : - 5 } }
81
80
>
82
81
< defs >
83
82
< linearGradient
@@ -122,7 +121,6 @@ export const GridItem: React.FC<IGridItemProps> = ({ metric, dir }) => {
122
121
height = { 80 }
123
122
flexDirection = "column"
124
123
justifyContent = "space-between"
125
- alignItems = "flex-start"
126
124
borderX = { {
127
125
base : "0px solid #000000" ,
128
126
lg : "1px solid" ,
@@ -146,52 +144,29 @@ export const GridItem: React.FC<IGridItemProps> = ({ metric, dir }) => {
146
144
</ Text >
147
145
< OldText > { description } </ OldText >
148
146
</ Box >
149
- { ! state . hasError && ! isLoading && (
150
- < >
151
- < Box
152
- position = "absolute"
153
- insetInlineStart = { 0 }
154
- bottom = { 0 }
155
- width = "100%"
156
- height = "65%"
157
- >
158
- { chart }
159
- </ Box >
160
- { dir === "rtl" ? (
161
- < Box
162
- position = "absolute"
163
- bottom = "20px"
164
- fontFamily = "monospace"
165
- insetInlineStart = "20px"
166
- >
167
- { buttonContainer }
168
- </ Box >
169
- ) : (
170
- < Box
171
- position = "absolute"
172
- bottom = "20px"
173
- fontFamily = "monospace"
174
- insetInlineEnd = "20px"
175
- >
176
- { buttonContainer }
177
- </ Box >
178
- ) }
179
- </ >
147
+ { hasData && (
148
+ < Box position = "absolute" insetInline = "0" bottom = { 0 } height = "65%" >
149
+ { chart }
150
+ </ Box >
180
151
) }
181
- < Box
182
- position = "absolute"
183
- bottom = "8%"
184
- fontSize = { { base : "max(8.8vw, 48px)" , lg : "min(4.4vw, 4rem)" } }
185
- fontWeight = { 600 }
186
- marginTop = { 0 }
187
- marginBottom = { 4 }
188
- color = "text"
189
- flexWrap = "wrap"
190
- textOverflow = "ellipsis"
191
- lineHeight = "1.6rem"
192
- >
193
- { value }
194
- </ Box >
152
+ < Flex justifyContent = "space-between" >
153
+ < Box
154
+ fontSize = { { base : "max(8.8vw, 48px)" , lg : "min(4.4vw, 4rem)" } }
155
+ fontWeight = { 600 }
156
+ color = "text"
157
+ flexWrap = "wrap"
158
+ textOverflow = "ellipsis"
159
+ lineHeight = "1.6rem"
160
+ mb = "2"
161
+ >
162
+ { value }
163
+ </ Box >
164
+ { hasData && (
165
+ < Box fontFamily = "monospace" me = "-1" mb = "-1" >
166
+ { buttonContainer }
167
+ </ Box >
168
+ ) }
169
+ </ Flex >
195
170
</ Flex >
196
171
)
197
172
}
0 commit comments