stats.tsx 6.7 KB

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