mirror of
https://github.com/status-im/fathom.git
synced 2025-02-28 19:10:36 +00:00
add visitor graph
This commit is contained in:
parent
5eae716a9c
commit
8d26212ddd
20
ROADMAP.md
Normal file
20
ROADMAP.md
Normal file
@ -0,0 +1,20 @@
|
||||
Ana Roadmap
|
||||
===========
|
||||
|
||||
This is a general draft document for thoughts and todo's.
|
||||
|
||||
### Key metrics
|
||||
|
||||
- Unique visits per day (in period)
|
||||
- Pageviews per day (in period)
|
||||
- Demographic
|
||||
- Country
|
||||
- Browser + version
|
||||
- Operating System
|
||||
- Acquisition
|
||||
|
||||
|
||||
### Admin themes
|
||||
|
||||
- Pages, http://pages.revox.io/dashboard/latest/html/index.html#usa
|
||||
- Metronic, http://keenthemes.com/preview/metronic/
|
@ -6,6 +6,7 @@ import (
|
||||
)
|
||||
|
||||
func RegisterRoutes() {
|
||||
http.HandleFunc("/api/visits/count/day", GetVisitsDayCountHandler)
|
||||
http.HandleFunc("/api/visits/count/realtime", GetVisitsRealtimeCount)
|
||||
http.HandleFunc("/api/visits", GetVisitsHandler)
|
||||
http.HandleFunc("/api/pageviews", GetPageviewsHandler)
|
||||
|
@ -11,7 +11,8 @@ import (
|
||||
func GetPageviewsHandler(w http.ResponseWriter, r *http.Request) {
|
||||
stmt, err := core.DB.Prepare(`SELECT
|
||||
path,
|
||||
COUNT(DISTINCT(ip_address)) AS pageviews
|
||||
COUNT(ip_address) AS pageviews,
|
||||
COUNT(DISTINCT(ip_address)) AS pageviews_unique
|
||||
FROM visits
|
||||
GROUP BY path`)
|
||||
checkError(err)
|
||||
@ -24,7 +25,7 @@ func GetPageviewsHandler(w http.ResponseWriter, r *http.Request) {
|
||||
defer rows.Close()
|
||||
for rows.Next() {
|
||||
var p models.Pageview
|
||||
err = rows.Scan(&p.Path, &p.Count);
|
||||
err = rows.Scan(&p.Path, &p.Count, &p.CountUnique);
|
||||
checkError(err)
|
||||
results = append(results, p)
|
||||
}
|
||||
|
@ -5,6 +5,7 @@ import (
|
||||
"github.com/dannyvankooten/ana/models"
|
||||
"github.com/dannyvankooten/ana/core"
|
||||
"encoding/json"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
// URL: /api/visits
|
||||
@ -18,12 +19,19 @@ func GetVisitsHandler(w http.ResponseWriter, r *http.Request) {
|
||||
path,
|
||||
COALESCE(screen_resolution, '') AS screen_resolution,
|
||||
timestamp
|
||||
FROM visits`)
|
||||
FROM visits
|
||||
ORDER BY timestamp DESC
|
||||
LIMIT ?`)
|
||||
|
||||
checkError(err)
|
||||
defer stmt.Close()
|
||||
|
||||
rows, err := stmt.Query()
|
||||
limit, _ := strconv.Atoi(r.URL.Query().Get("limit"))
|
||||
if limit == 0 {
|
||||
limit = 10
|
||||
}
|
||||
|
||||
rows, err := stmt.Query(&limit)
|
||||
checkError(err)
|
||||
|
||||
results := make([]models.Visit, 0)
|
||||
@ -44,10 +52,43 @@ func GetVisitsHandler(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
// URL: /api/visits/count/realtime
|
||||
func GetVisitsRealtimeCount(w http.ResponseWriter, r *http.Request) {
|
||||
row := core.DB.QueryRow(`SELECT COUNT(DISTINCT(id)) FROM visits WHERE timestamp >= DATE_SUB(CURRENT_TIMESTAMP, INTERVAL 3 HOUR_MINUTE)`)
|
||||
row := core.DB.QueryRow(`SELECT COUNT(DISTINCT(ip_address)) FROM visits WHERE timestamp >= DATE_SUB(CURRENT_TIMESTAMP, INTERVAL 3 HOUR_MINUTE)`)
|
||||
var result int
|
||||
row.Scan(&result)
|
||||
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
json.NewEncoder(w).Encode(result)
|
||||
}
|
||||
|
||||
// URL: /api/visits/count/day
|
||||
func GetVisitsDayCountHandler(w http.ResponseWriter, r *http.Request) {
|
||||
stmt, err := core.DB.Prepare(`SELECT
|
||||
COUNT(*) AS count, DATE_FORMAT(timestamp, '%Y-%m-%d') AS date_group
|
||||
FROM visits
|
||||
GROUP BY date_group`)
|
||||
checkError(err)
|
||||
defer stmt.Close()
|
||||
|
||||
rows, err := stmt.Query()
|
||||
checkError(err)
|
||||
|
||||
type Datapoint struct {
|
||||
Count int
|
||||
Label string
|
||||
}
|
||||
|
||||
results := make([]Datapoint, 0)
|
||||
defer rows.Close()
|
||||
for rows.Next() {
|
||||
v := Datapoint{}
|
||||
err = rows.Scan(&v.Count, &v.Label);
|
||||
checkError(err)
|
||||
results = append(results, v)
|
||||
}
|
||||
|
||||
err = rows.Err();
|
||||
checkError(err)
|
||||
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
json.NewEncoder(w).Encode(results)
|
||||
}
|
||||
|
@ -19,20 +19,22 @@ class PageviewsList extends React.Component {
|
||||
const tableRows = this.state.records.map((p, i) =>
|
||||
<tr key={i}>
|
||||
<td>{i+1}</td>
|
||||
<td>{p.Path}</td>
|
||||
<td><a href="{p.Path}">{p.Path}</a></td>
|
||||
<td>{p.Count}</td>
|
||||
<td>{p.CountUnique}</td>
|
||||
</tr>
|
||||
);
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div className="block">
|
||||
<h2>Pageviews</h2>
|
||||
<table className="table pageviews">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>#</th>
|
||||
<th>Path</th>
|
||||
<th>Count</th>
|
||||
<th>URL</th>
|
||||
<th>Pageviews</th>
|
||||
<th>Unique</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>{tableRows}</tbody>
|
||||
|
@ -1,3 +1,5 @@
|
||||
'use strict';
|
||||
|
||||
import React, { Component } from 'react'
|
||||
|
||||
class RealtimeVisitsCount extends React.Component {
|
||||
|
75
assets/js/components/visits-graph.js
Normal file
75
assets/js/components/visits-graph.js
Normal file
@ -0,0 +1,75 @@
|
||||
'use strict';
|
||||
|
||||
import React, { Component } from 'react'
|
||||
import Chart from 'chart.js'
|
||||
|
||||
function rand(min, max, num) {
|
||||
var rtn = [];
|
||||
while (rtn.length < num) {
|
||||
rtn.push((Math.random() * (max - min)) + min);
|
||||
}
|
||||
return rtn;
|
||||
}
|
||||
|
||||
class VisitsGraph extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = { data: [] }
|
||||
this.refresh();
|
||||
}
|
||||
|
||||
refresh() {
|
||||
return fetch('/api/visits/count/day')
|
||||
.then((r) => r.json())
|
||||
.then((data) => {
|
||||
this.setState({ data: data });
|
||||
this.initChart(this.chartCtx);
|
||||
});
|
||||
}
|
||||
|
||||
initChart(ctx) {
|
||||
if( ! this.state.data.length ) {
|
||||
return;
|
||||
}
|
||||
|
||||
console.log(this.state.data.map((d) => d.Label));
|
||||
console.log(this.state.data.map((d) => d.Count ));
|
||||
console.log("Init chart");
|
||||
console.log(ctx);
|
||||
|
||||
var myChart = new Chart(ctx, {
|
||||
type: 'line',
|
||||
data: {
|
||||
labels: this.state.data.map((d) => d.Label),
|
||||
datasets: [{
|
||||
label: '# of Visitors',
|
||||
data: this.state.data.map((d) => d.Count),
|
||||
backgroundColor: 'rgba(162, 52, 235, 0.2)'
|
||||
}]
|
||||
},
|
||||
options: {
|
||||
tooltips: {
|
||||
xPadding: 12,
|
||||
yPadding: 9,
|
||||
},
|
||||
scales: {
|
||||
yAxes: [{
|
||||
ticks: {
|
||||
beginAtZero:true
|
||||
}
|
||||
}]
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<div className="block">
|
||||
<canvas ref={(ctx) => { this.chartCtx = ctx; }} width="600" height="200"></canvas>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default VisitsGraph;
|
@ -25,7 +25,7 @@ class VisitsList extends React.Component {
|
||||
);
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div className="block">
|
||||
<h2>Visits</h2>
|
||||
<table className="visits-table">
|
||||
<thead>
|
||||
|
@ -5,20 +5,14 @@ import ReactDOM from 'react-dom';
|
||||
import RealtimeVisitsCount from './components/realtime-visits.js';
|
||||
import VisitsList from './components/visits-list.js';
|
||||
import PageviewsList from './components/pageviews.js';
|
||||
import VisitsGraph from './components/visits-graph.js';
|
||||
|
||||
function tick() {
|
||||
const element = (
|
||||
<div className="container">
|
||||
<h1>Ana</h1>
|
||||
<RealtimeVisitsCount />
|
||||
<PageviewsList />
|
||||
<VisitsList />
|
||||
</div>
|
||||
);
|
||||
|
||||
ReactDOM.render(
|
||||
element, document.getElementById('root')
|
||||
);
|
||||
}
|
||||
|
||||
tick() && window.setInterval(tick, 1000);
|
||||
ReactDOM.render(
|
||||
<div className="container">
|
||||
<h1>Ana</h1>
|
||||
<RealtimeVisitsCount />
|
||||
<VisitsGraph />
|
||||
<PageviewsList />
|
||||
</div>,
|
||||
document.getElementById('root')
|
||||
);
|
||||
|
461
assets/sass/_normalize.scss
Normal file
461
assets/sass/_normalize.scss
Normal file
@ -0,0 +1,461 @@
|
||||
/*! normalize.css v5.0.0 | MIT License | github.com/necolas/normalize.css */
|
||||
|
||||
/**
|
||||
* 1. Change the default font family in all browsers (opinionated).
|
||||
* 2. Correct the line height in all browsers.
|
||||
* 3. Prevent adjustments of font size after orientation changes in
|
||||
* IE on Windows Phone and in iOS.
|
||||
*/
|
||||
|
||||
/* Document
|
||||
========================================================================== */
|
||||
|
||||
html {
|
||||
font-family: sans-serif; /* 1 */
|
||||
line-height: 1.15; /* 2 */
|
||||
-ms-text-size-adjust: 100%; /* 3 */
|
||||
-webkit-text-size-adjust: 100%; /* 3 */
|
||||
}
|
||||
|
||||
/* Sections
|
||||
========================================================================== */
|
||||
|
||||
/**
|
||||
* Remove the margin in all browsers (opinionated).
|
||||
*/
|
||||
|
||||
body {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the correct display in IE 9-.
|
||||
*/
|
||||
|
||||
article,
|
||||
aside,
|
||||
footer,
|
||||
header,
|
||||
nav,
|
||||
section {
|
||||
display: block;
|
||||
}
|
||||
|
||||
/**
|
||||
* Correct the font size and margin on `h1` elements within `section` and
|
||||
* `article` contexts in Chrome, Firefox, and Safari.
|
||||
*/
|
||||
|
||||
h1 {
|
||||
font-size: 2em;
|
||||
margin: 0.67em 0;
|
||||
}
|
||||
|
||||
/* Grouping content
|
||||
========================================================================== */
|
||||
|
||||
/**
|
||||
* Add the correct display in IE 9-.
|
||||
* 1. Add the correct display in IE.
|
||||
*/
|
||||
|
||||
figcaption,
|
||||
figure,
|
||||
main { /* 1 */
|
||||
display: block;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the correct margin in IE 8.
|
||||
*/
|
||||
|
||||
figure {
|
||||
margin: 1em 40px;
|
||||
}
|
||||
|
||||
/**
|
||||
* 1. Add the correct box sizing in Firefox.
|
||||
* 2. Show the overflow in Edge and IE.
|
||||
*/
|
||||
|
||||
hr {
|
||||
box-sizing: content-box; /* 1 */
|
||||
height: 0; /* 1 */
|
||||
overflow: visible; /* 2 */
|
||||
}
|
||||
|
||||
/**
|
||||
* 1. Correct the inheritance and scaling of font size in all browsers.
|
||||
* 2. Correct the odd `em` font sizing in all browsers.
|
||||
*/
|
||||
|
||||
pre {
|
||||
font-family: monospace, monospace; /* 1 */
|
||||
font-size: 1em; /* 2 */
|
||||
}
|
||||
|
||||
/* Text-level semantics
|
||||
========================================================================== */
|
||||
|
||||
/**
|
||||
* 1. Remove the gray background on active links in IE 10.
|
||||
* 2. Remove gaps in links underline in iOS 8+ and Safari 8+.
|
||||
*/
|
||||
|
||||
a {
|
||||
background-color: transparent; /* 1 */
|
||||
-webkit-text-decoration-skip: objects; /* 2 */
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the outline on focused links when they are also active or hovered
|
||||
* in all browsers (opinionated).
|
||||
*/
|
||||
|
||||
a:active,
|
||||
a:hover {
|
||||
outline-width: 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* 1. Remove the bottom border in Firefox 39-.
|
||||
* 2. Add the correct text decoration in Chrome, Edge, IE, Opera, and Safari.
|
||||
*/
|
||||
|
||||
abbr[title] {
|
||||
border-bottom: none; /* 1 */
|
||||
text-decoration: underline; /* 2 */
|
||||
text-decoration: underline dotted; /* 2 */
|
||||
}
|
||||
|
||||
/**
|
||||
* Prevent the duplicate application of `bolder` by the next rule in Safari 6.
|
||||
*/
|
||||
|
||||
b,
|
||||
strong {
|
||||
font-weight: inherit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the correct font weight in Chrome, Edge, and Safari.
|
||||
*/
|
||||
|
||||
b,
|
||||
strong {
|
||||
font-weight: bolder;
|
||||
}
|
||||
|
||||
/**
|
||||
* 1. Correct the inheritance and scaling of font size in all browsers.
|
||||
* 2. Correct the odd `em` font sizing in all browsers.
|
||||
*/
|
||||
|
||||
code,
|
||||
kbd,
|
||||
samp {
|
||||
font-family: monospace, monospace; /* 1 */
|
||||
font-size: 1em; /* 2 */
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the correct font style in Android 4.3-.
|
||||
*/
|
||||
|
||||
dfn {
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the correct background and color in IE 9-.
|
||||
*/
|
||||
|
||||
mark {
|
||||
background-color: #ff0;
|
||||
color: #000;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the correct font size in all browsers.
|
||||
*/
|
||||
|
||||
small {
|
||||
font-size: 80%;
|
||||
}
|
||||
|
||||
/**
|
||||
* Prevent `sub` and `sup` elements from affecting the line height in
|
||||
* all browsers.
|
||||
*/
|
||||
|
||||
sub,
|
||||
sup {
|
||||
font-size: 75%;
|
||||
line-height: 0;
|
||||
position: relative;
|
||||
vertical-align: baseline;
|
||||
}
|
||||
|
||||
sub {
|
||||
bottom: -0.25em;
|
||||
}
|
||||
|
||||
sup {
|
||||
top: -0.5em;
|
||||
}
|
||||
|
||||
/* Embedded content
|
||||
========================================================================== */
|
||||
|
||||
/**
|
||||
* Add the correct display in IE 9-.
|
||||
*/
|
||||
|
||||
audio,
|
||||
video {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the correct display in iOS 4-7.
|
||||
*/
|
||||
|
||||
audio:not([controls]) {
|
||||
display: none;
|
||||
height: 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the border on images inside links in IE 10-.
|
||||
*/
|
||||
|
||||
img {
|
||||
border-style: none;
|
||||
}
|
||||
|
||||
/**
|
||||
* Hide the overflow in IE.
|
||||
*/
|
||||
|
||||
svg:not(:root) {
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
/* Forms
|
||||
========================================================================== */
|
||||
|
||||
/**
|
||||
* 1. Change the font styles in all browsers (opinionated).
|
||||
* 2. Remove the margin in Firefox and Safari.
|
||||
*/
|
||||
|
||||
button,
|
||||
input,
|
||||
optgroup,
|
||||
select,
|
||||
textarea {
|
||||
font-family: sans-serif; /* 1 */
|
||||
font-size: 100%; /* 1 */
|
||||
line-height: 1.15; /* 1 */
|
||||
margin: 0; /* 2 */
|
||||
}
|
||||
|
||||
/**
|
||||
* Show the overflow in IE.
|
||||
* 1. Show the overflow in Edge.
|
||||
*/
|
||||
|
||||
button,
|
||||
input { /* 1 */
|
||||
overflow: visible;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the inheritance of text transform in Edge, Firefox, and IE.
|
||||
* 1. Remove the inheritance of text transform in Firefox.
|
||||
*/
|
||||
|
||||
button,
|
||||
select { /* 1 */
|
||||
text-transform: none;
|
||||
}
|
||||
|
||||
/**
|
||||
* 1. Prevent a WebKit bug where (2) destroys native `audio` and `video`
|
||||
* controls in Android 4.
|
||||
* 2. Correct the inability to style clickable types in iOS and Safari.
|
||||
*/
|
||||
|
||||
button,
|
||||
html [type="button"], /* 1 */
|
||||
[type="reset"],
|
||||
[type="submit"] {
|
||||
-webkit-appearance: button; /* 2 */
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the inner border and padding in Firefox.
|
||||
*/
|
||||
|
||||
button::-moz-focus-inner,
|
||||
[type="button"]::-moz-focus-inner,
|
||||
[type="reset"]::-moz-focus-inner,
|
||||
[type="submit"]::-moz-focus-inner {
|
||||
border-style: none;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Restore the focus styles unset by the previous rule.
|
||||
*/
|
||||
|
||||
button:-moz-focusring,
|
||||
[type="button"]:-moz-focusring,
|
||||
[type="reset"]:-moz-focusring,
|
||||
[type="submit"]:-moz-focusring {
|
||||
outline: 1px dotted ButtonText;
|
||||
}
|
||||
|
||||
/**
|
||||
* Change the border, margin, and padding in all browsers (opinionated).
|
||||
*/
|
||||
|
||||
fieldset {
|
||||
border: 1px solid #c0c0c0;
|
||||
margin: 0 2px;
|
||||
padding: 0.35em 0.625em 0.75em;
|
||||
}
|
||||
|
||||
/**
|
||||
* 1. Correct the text wrapping in Edge and IE.
|
||||
* 2. Correct the color inheritance from `fieldset` elements in IE.
|
||||
* 3. Remove the padding so developers are not caught out when they zero out
|
||||
* `fieldset` elements in all browsers.
|
||||
*/
|
||||
|
||||
legend {
|
||||
box-sizing: border-box; /* 1 */
|
||||
color: inherit; /* 2 */
|
||||
display: table; /* 1 */
|
||||
max-width: 100%; /* 1 */
|
||||
padding: 0; /* 3 */
|
||||
white-space: normal; /* 1 */
|
||||
}
|
||||
|
||||
/**
|
||||
* 1. Add the correct display in IE 9-.
|
||||
* 2. Add the correct vertical alignment in Chrome, Firefox, and Opera.
|
||||
*/
|
||||
|
||||
progress {
|
||||
display: inline-block; /* 1 */
|
||||
vertical-align: baseline; /* 2 */
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the default vertical scrollbar in IE.
|
||||
*/
|
||||
|
||||
textarea {
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
/**
|
||||
* 1. Add the correct box sizing in IE 10-.
|
||||
* 2. Remove the padding in IE 10-.
|
||||
*/
|
||||
|
||||
[type="checkbox"],
|
||||
[type="radio"] {
|
||||
box-sizing: border-box; /* 1 */
|
||||
padding: 0; /* 2 */
|
||||
}
|
||||
|
||||
/**
|
||||
* Correct the cursor style of increment and decrement buttons in Chrome.
|
||||
*/
|
||||
|
||||
[type="number"]::-webkit-inner-spin-button,
|
||||
[type="number"]::-webkit-outer-spin-button {
|
||||
height: auto;
|
||||
}
|
||||
|
||||
/**
|
||||
* 1. Correct the odd appearance in Chrome and Safari.
|
||||
* 2. Correct the outline style in Safari.
|
||||
*/
|
||||
|
||||
[type="search"] {
|
||||
-webkit-appearance: textfield; /* 1 */
|
||||
outline-offset: -2px; /* 2 */
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the inner padding and cancel buttons in Chrome and Safari on macOS.
|
||||
*/
|
||||
|
||||
[type="search"]::-webkit-search-cancel-button,
|
||||
[type="search"]::-webkit-search-decoration {
|
||||
-webkit-appearance: none;
|
||||
}
|
||||
|
||||
/**
|
||||
* 1. Correct the inability to style clickable types in iOS and Safari.
|
||||
* 2. Change font properties to `inherit` in Safari.
|
||||
*/
|
||||
|
||||
::-webkit-file-upload-button {
|
||||
-webkit-appearance: button; /* 1 */
|
||||
font: inherit; /* 2 */
|
||||
}
|
||||
|
||||
/* Interactive
|
||||
========================================================================== */
|
||||
|
||||
/*
|
||||
* Add the correct display in IE 9-.
|
||||
* 1. Add the correct display in Edge, IE, and Firefox.
|
||||
*/
|
||||
|
||||
details, /* 1 */
|
||||
menu {
|
||||
display: block;
|
||||
}
|
||||
|
||||
/*
|
||||
* Add the correct display in all browsers.
|
||||
*/
|
||||
|
||||
summary {
|
||||
display: list-item;
|
||||
}
|
||||
|
||||
/* Scripting
|
||||
========================================================================== */
|
||||
|
||||
/**
|
||||
* Add the correct display in IE 9-.
|
||||
*/
|
||||
|
||||
canvas {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the correct display in IE.
|
||||
*/
|
||||
|
||||
template {
|
||||
display: none;
|
||||
}
|
||||
|
||||
/* Hidden
|
||||
========================================================================== */
|
||||
|
||||
/**
|
||||
* Add the correct display in IE 10-.
|
||||
*/
|
||||
|
||||
[hidden] {
|
||||
display: none;
|
||||
}
|
@ -1,7 +1,10 @@
|
||||
@import "normalize";
|
||||
|
||||
body {
|
||||
font-family: Arial, Verdana, sans-serif;
|
||||
font-size: 14px;
|
||||
color: #333;
|
||||
background: #f5f5f5;
|
||||
}
|
||||
|
||||
h1, h2, h3 {
|
||||
@ -22,6 +25,16 @@ table {
|
||||
}
|
||||
}
|
||||
|
||||
.block {
|
||||
width: auto;
|
||||
background: white;
|
||||
padding: 20px;
|
||||
margin-bottom: 20px;
|
||||
|
||||
h1, h2, h3 {
|
||||
margin-top: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.count {
|
||||
font-size: 24px;
|
||||
|
@ -3,4 +3,5 @@ package models
|
||||
type Pageview struct {
|
||||
Path string
|
||||
Count int
|
||||
CountUnique int
|
||||
}
|
||||
|
@ -12,6 +12,7 @@
|
||||
"babel-preset-es2015": "^6.18.0",
|
||||
"babel-preset-react": "^6.16.0",
|
||||
"babelify": "^7.3.0",
|
||||
"chart.js": "^2.4.0",
|
||||
"gulp-util": "^3.0.7",
|
||||
"react-dom": "^15.4.0"
|
||||
}
|
||||
|
32
yarn.lock
32
yarn.lock
@ -842,6 +842,26 @@ chalk@^1.0.0, chalk@^1.1.0, chalk@^1.1.1:
|
||||
strip-ansi "^3.0.0"
|
||||
supports-color "^2.0.0"
|
||||
|
||||
chart.js:
|
||||
version "2.4.0"
|
||||
resolved "https://registry.yarnpkg.com/chart.js/-/chart.js-2.4.0.tgz#44198073f0f43e5e16662e108420d92652a3c9a3"
|
||||
dependencies:
|
||||
chartjs-color "^2.0.0"
|
||||
moment "^2.10.6"
|
||||
|
||||
chartjs-color-string@^0.4.0:
|
||||
version "0.4.0"
|
||||
resolved "https://registry.yarnpkg.com/chartjs-color-string/-/chartjs-color-string-0.4.0.tgz#57748d4530ae28d8db0a5492182ba06dfdf2f468"
|
||||
dependencies:
|
||||
color-name "^1.0.0"
|
||||
|
||||
chartjs-color@^2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/chartjs-color/-/chartjs-color-2.0.0.tgz#7f60c7256589b24914814ece757659117381e35b"
|
||||
dependencies:
|
||||
chartjs-color-string "^0.4.0"
|
||||
color-convert "^0.5.3"
|
||||
|
||||
cipher-base@^1.0.0, cipher-base@^1.0.1:
|
||||
version "1.0.3"
|
||||
resolved "https://registry.yarnpkg.com/cipher-base/-/cipher-base-1.0.3.tgz#eeabf194419ce900da3018c207d212f2a6df0a07"
|
||||
@ -872,6 +892,14 @@ code-point-at@^1.0.0:
|
||||
version "1.1.0"
|
||||
resolved "https://registry.yarnpkg.com/code-point-at/-/code-point-at-1.1.0.tgz#0d070b4d043a5bea33a2f1a40e2edb3d9a4ccf77"
|
||||
|
||||
color-convert@^0.5.3:
|
||||
version "0.5.3"
|
||||
resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-0.5.3.tgz#bdb6c69ce660fadffe0b0007cc447e1b9f7282bd"
|
||||
|
||||
color-name@^1.0.0:
|
||||
version "1.1.1"
|
||||
resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.1.tgz#4b1415304cf50028ea81643643bd82ea05803689"
|
||||
|
||||
combine-source-map@~0.7.1:
|
||||
version "0.7.2"
|
||||
resolved "https://registry.yarnpkg.com/combine-source-map/-/combine-source-map-0.7.2.tgz#0870312856b307a87cc4ac486f3a9a62aeccc09e"
|
||||
@ -2276,6 +2304,10 @@ module-deps@^4.0.8:
|
||||
through2 "^2.0.0"
|
||||
xtend "^4.0.0"
|
||||
|
||||
moment@^2.10.6:
|
||||
version "2.17.0"
|
||||
resolved "https://registry.yarnpkg.com/moment/-/moment-2.17.0.tgz#a4c292e02aac5ddefb29a6eed24f51938dd3b74f"
|
||||
|
||||
ms@0.7.2:
|
||||
version "0.7.2"
|
||||
resolved "https://registry.yarnpkg.com/ms/-/ms-0.7.2.tgz#ae25cf2512b3885a1d95d7f037868d8431124765"
|
||||
|
Loading…
x
Reference in New Issue
Block a user