analytics.tsx 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189
  1. import {
  2. Card,
  3. CardContent,
  4. CardDescription,
  5. CardHeader,
  6. CardTitle,
  7. } from '@/components/ui/card'
  8. import { AnalyticsChart } from './analytics-chart'
  9. export function Analytics() {
  10. return (
  11. <div className='space-y-4'>
  12. <Card>
  13. <CardHeader>
  14. <CardTitle>Traffic Overview</CardTitle>
  15. <CardDescription>Weekly clicks and unique visitors</CardDescription>
  16. </CardHeader>
  17. <CardContent className='px-6'>
  18. <AnalyticsChart />
  19. </CardContent>
  20. </Card>
  21. <div className='grid gap-4 sm:grid-cols-2 lg:grid-cols-4'>
  22. <Card>
  23. <CardHeader className='flex flex-row items-center justify-between space-y-0 pb-2'>
  24. <CardTitle className='text-sm font-medium'>Total Clicks</CardTitle>
  25. <svg
  26. xmlns='http://www.w3.org/2000/svg'
  27. viewBox='0 0 24 24'
  28. fill='none'
  29. stroke='currentColor'
  30. strokeLinecap='round'
  31. strokeLinejoin='round'
  32. strokeWidth='2'
  33. className='text-muted-foreground h-4 w-4'
  34. >
  35. <path d='M3 3v18h18' />
  36. <path d='M7 15l4-4 4 4 4-6' />
  37. </svg>
  38. </CardHeader>
  39. <CardContent>
  40. <div className='text-2xl font-bold'>1,248</div>
  41. <p className='text-muted-foreground text-xs'>+12.4% vs last week</p>
  42. </CardContent>
  43. </Card>
  44. <Card>
  45. <CardHeader className='flex flex-row items-center justify-between space-y-0 pb-2'>
  46. <CardTitle className='text-sm font-medium'>
  47. Unique Visitors
  48. </CardTitle>
  49. <svg
  50. xmlns='http://www.w3.org/2000/svg'
  51. viewBox='0 0 24 24'
  52. fill='none'
  53. stroke='currentColor'
  54. strokeLinecap='round'
  55. strokeLinejoin='round'
  56. strokeWidth='2'
  57. className='text-muted-foreground h-4 w-4'
  58. >
  59. <circle cx='12' cy='7' r='4' />
  60. <path d='M6 21v-2a6 6 0 0 1 12 0v2' />
  61. </svg>
  62. </CardHeader>
  63. <CardContent>
  64. <div className='text-2xl font-bold'>832</div>
  65. <p className='text-muted-foreground text-xs'>+5.8% vs last week</p>
  66. </CardContent>
  67. </Card>
  68. <Card>
  69. <CardHeader className='flex flex-row items-center justify-between space-y-0 pb-2'>
  70. <CardTitle className='text-sm font-medium'>Bounce Rate</CardTitle>
  71. <svg
  72. xmlns='http://www.w3.org/2000/svg'
  73. viewBox='0 0 24 24'
  74. fill='none'
  75. stroke='currentColor'
  76. strokeLinecap='round'
  77. strokeLinejoin='round'
  78. strokeWidth='2'
  79. className='text-muted-foreground h-4 w-4'
  80. >
  81. <path d='M3 12h6l3 6 3-6h6' />
  82. </svg>
  83. </CardHeader>
  84. <CardContent>
  85. <div className='text-2xl font-bold'>42%</div>
  86. <p className='text-muted-foreground text-xs'>-3.2% vs last week</p>
  87. </CardContent>
  88. </Card>
  89. <Card>
  90. <CardHeader className='flex flex-row items-center justify-between space-y-0 pb-2'>
  91. <CardTitle className='text-sm font-medium'>Avg. Session</CardTitle>
  92. <svg
  93. xmlns='http://www.w3.org/2000/svg'
  94. viewBox='0 0 24 24'
  95. fill='none'
  96. stroke='currentColor'
  97. strokeLinecap='round'
  98. strokeLinejoin='round'
  99. strokeWidth='2'
  100. className='text-muted-foreground h-4 w-4'
  101. >
  102. <circle cx='12' cy='12' r='10' />
  103. <path d='M12 6v6l4 2' />
  104. </svg>
  105. </CardHeader>
  106. <CardContent>
  107. <div className='text-2xl font-bold'>3m 24s</div>
  108. <p className='text-muted-foreground text-xs'>+18s vs last week</p>
  109. </CardContent>
  110. </Card>
  111. </div>
  112. <div className='grid grid-cols-1 gap-4 lg:grid-cols-7'>
  113. <Card className='col-span-1 lg:col-span-4'>
  114. <CardHeader>
  115. <CardTitle>Referrers</CardTitle>
  116. <CardDescription>Top sources driving traffic</CardDescription>
  117. </CardHeader>
  118. <CardContent>
  119. <SimpleBarList
  120. items={[
  121. { name: 'Direct', value: 512 },
  122. { name: 'Product Hunt', value: 238 },
  123. { name: 'Twitter', value: 174 },
  124. { name: 'Blog', value: 104 },
  125. ]}
  126. barClass='bg-primary'
  127. valueFormatter={(n) => `${n}`}
  128. />
  129. </CardContent>
  130. </Card>
  131. <Card className='col-span-1 lg:col-span-3'>
  132. <CardHeader>
  133. <CardTitle>Devices</CardTitle>
  134. <CardDescription>How users access your app</CardDescription>
  135. </CardHeader>
  136. <CardContent>
  137. <SimpleBarList
  138. items={[
  139. { name: 'Desktop', value: 74 },
  140. { name: 'Mobile', value: 22 },
  141. { name: 'Tablet', value: 4 },
  142. ]}
  143. barClass='bg-muted-foreground'
  144. valueFormatter={(n) => `${n}%`}
  145. />
  146. </CardContent>
  147. </Card>
  148. </div>
  149. </div>
  150. )
  151. }
  152. function SimpleBarList({
  153. items,
  154. valueFormatter,
  155. barClass,
  156. }: {
  157. items: { name: string; value: number }[]
  158. valueFormatter: (n: number) => string
  159. barClass: string
  160. }) {
  161. const max = Math.max(...items.map((i) => i.value), 1)
  162. return (
  163. <ul className='space-y-3'>
  164. {items.map((i) => {
  165. const width = `${Math.round((i.value / max) * 100)}%`
  166. return (
  167. <li key={i.name} className='flex items-center justify-between gap-3'>
  168. <div className='min-w-0 flex-1'>
  169. <div className='text-muted-foreground mb-1 truncate text-xs'>
  170. {i.name}
  171. </div>
  172. <div className='bg-muted h-2.5 w-full rounded-full'>
  173. <div
  174. className={`h-2.5 rounded-full ${barClass}`}
  175. style={{ width }}
  176. />
  177. </div>
  178. </div>
  179. <div className='ps-2 text-xs font-medium tabular-nums'>
  180. {valueFormatter(i.value)}
  181. </div>
  182. </li>
  183. )
  184. })}
  185. </ul>
  186. )
  187. }