stats.tsx 6.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213
  1. import { Box, Flex } from "reflexbox/styled-components";
  2. import React, { useState, useEffect } from "react";
  3. import formatDate from "date-fns/format";
  4. import { NextPage } from "next";
  5. import Link from "next/link";
  6. import axios from "axios";
  7. import Text, { H1, H2, H4, Span } from "../components/Text";
  8. import { getAxiosConfig, removeProtocol } from "../utils";
  9. import { Button, NavButton } from "../components/Button";
  10. import { Col, RowCenterV } from "../components/Layout";
  11. import { Area, Bar, Pie } from "../components/Charts";
  12. import PageLoading from "../components/PageLoading";
  13. import AppWrapper from "../components/AppWrapper";
  14. import Divider from "../components/Divider";
  15. import { useStoreState } from "../store";
  16. import ALink from "../components/ALink";
  17. import { APIv2, Colors } from "../consts";
  18. import Icon from "../components/Icon";
  19. interface Props {
  20. id?: string;
  21. }
  22. const StatsPage: NextPage<Props> = ({ id }) => {
  23. const { isAuthenticated } = useStoreState(s => s.auth);
  24. const [loading, setLoading] = useState(true);
  25. const [error, setError] = useState(false);
  26. const [data, setData] = useState();
  27. const [period, setPeriod] = useState("lastDay");
  28. const stats = data && data[period];
  29. useEffect(() => {
  30. if (!id || !isAuthenticated) return;
  31. axios
  32. .get(`${APIv2.Links}/${id}/stats`, getAxiosConfig())
  33. .then(({ data }) => {
  34. setLoading(false);
  35. setError(!data);
  36. setData(data);
  37. })
  38. .catch(() => {
  39. setLoading(false);
  40. setError(true);
  41. });
  42. }, []);
  43. let errorMessage;
  44. if (!isAuthenticated) {
  45. errorMessage = (
  46. <Flex mt={3}>
  47. <Icon name="x" size={32} mr={3} stroke={Colors.TrashIcon} />
  48. <H2>You need to login to view stats.</H2>
  49. </Flex>
  50. );
  51. }
  52. if (!id || error) {
  53. errorMessage = (
  54. <Flex mt={3}>
  55. <Icon name="x" size={32} mr={3} stroke={Colors.TrashIcon} />
  56. <H2>Couldn't get stats.</H2>
  57. </Flex>
  58. );
  59. }
  60. const loader = loading && <PageLoading />;
  61. const total = stats && stats.views.reduce((sum, view) => sum + view, 0);
  62. const periodText = period.includes("last")
  63. ? `the last ${period.replace("last", "").toLocaleLowerCase()}`
  64. : "all time";
  65. return (
  66. <AppWrapper>
  67. {errorMessage ||
  68. loader ||
  69. (data && (
  70. <Col width={1200} maxWidth="95%" alignItems="stretch" m="40px 0">
  71. <Flex justifyContent="space-between" alignItems="center" mb={3}>
  72. <H1 fontSize={[18, 20, 24]} light>
  73. Stats for:{" "}
  74. <ALink href={data.link} title="Short link">
  75. {removeProtocol(data.link)}
  76. </ALink>
  77. </H1>
  78. <Text fontSize={[13, 14]} textAlign="right">
  79. {data.target.length > 80
  80. ? `${data.target
  81. .split("")
  82. .slice(0, 80)
  83. .join("")}...`
  84. : data.target}
  85. </Text>
  86. </Flex>
  87. <Col
  88. backgroundColor="white"
  89. style={{
  90. borderRadius: 12,
  91. boxShadow: "0 6px 15px hsla(200, 20%, 70%, 0.3)",
  92. overflow: "hidden"
  93. }}
  94. >
  95. <RowCenterV
  96. flex="1 1 auto"
  97. backgroundColor={Colors.TableHeadBg}
  98. justifyContent="space-between"
  99. py={[3, 3, 24]}
  100. px={[3, 4]}
  101. >
  102. <H4>
  103. Total clicks: <Span bold>{data.total}</Span>
  104. </H4>
  105. <Flex>
  106. {[
  107. ["allTime", "All Time"],
  108. ["lastMonth", "Month"],
  109. ["lastWeek", "Week"],
  110. ["lastDay", "Day"]
  111. ].map(([p, n]) => (
  112. <NavButton
  113. ml={10}
  114. disabled={p === period}
  115. onClick={() => setPeriod(p as any)}
  116. key={p}
  117. >
  118. {n}
  119. </NavButton>
  120. ))}
  121. </Flex>
  122. </RowCenterV>
  123. <Col p={[3, 4]}>
  124. <H2 mb={2} light>
  125. <Span
  126. style={{
  127. borderBottom: `1px dotted ${Colors.StatsTotalUnderline}`
  128. }}
  129. bold
  130. >
  131. {total}
  132. </Span>{" "}
  133. tracked clicks in {periodText}.
  134. </H2>
  135. <Text fontSize={[13, 14]} color={Colors.StatsLastUpdateText}>
  136. Last update in{" "}
  137. {formatDate(new Date(data.updatedAt), "hh:mm aa")}
  138. </Text>
  139. <Flex width={1} mt={4}>
  140. <Area data={stats.views} period={period} />
  141. </Flex>
  142. {total > 0 && (
  143. <>
  144. <Divider my={4} />
  145. <Flex width={1}>
  146. <Col flex="1 1 0">
  147. <H2 mb={3} light>
  148. Referrals.
  149. </H2>
  150. <Pie data={stats.stats.referrer} />
  151. </Col>
  152. <Col flex="1 1 0">
  153. <H2 mb={3} light>
  154. Browsers.
  155. </H2>
  156. <Bar data={stats.stats.browser} />
  157. </Col>
  158. </Flex>
  159. <Divider my={4} />
  160. <Flex width={1}>
  161. <Col flex="1 1 0">
  162. <H2 mb={3} light>
  163. Country.
  164. </H2>
  165. <Pie data={stats.stats.country} />
  166. </Col>
  167. <Col flex="1 1 0">
  168. <H2 mb={3} light>
  169. OS.
  170. </H2>
  171. <Bar data={stats.stats.os} />
  172. </Col>
  173. </Flex>
  174. </>
  175. )}
  176. </Col>
  177. </Col>
  178. <Box alignSelf="center" my={64}>
  179. <Link href="/">
  180. <ALink href="/" title="Back to homepage" forButton>
  181. <Button>
  182. <Icon name="arrowLeft" stroke="white" mr={2} />
  183. Back to homepage
  184. </Button>
  185. </ALink>
  186. </Link>
  187. </Box>
  188. </Col>
  189. ))}
  190. </AppWrapper>
  191. );
  192. };
  193. StatsPage.getInitialProps = ({ query }) => {
  194. return Promise.resolve(query);
  195. };
  196. StatsPage.defaultProps = {
  197. id: ""
  198. };
  199. export default StatsPage;