mirror of https://github.com/status-im/fathom.git
add period date picker
This commit is contained in:
parent
11ad2d5da4
commit
5c39e1f1df
|
@ -14,11 +14,17 @@ var GetPageviewsHandler = http.HandlerFunc(func(w http.ResponseWriter, r *http.R
|
|||
COUNT(ip_address) AS pageviews,
|
||||
COUNT(DISTINCT(ip_address)) AS pageviews_unique
|
||||
FROM visits
|
||||
WHERE timestamp >= DATE_SUB(CURRENT_TIMESTAMP, INTERVAL ? DAY)
|
||||
GROUP BY path`)
|
||||
checkError(err)
|
||||
defer stmt.Close()
|
||||
|
||||
rows, err := stmt.Query()
|
||||
period := r.URL.Query().Get("period")
|
||||
if period == "" {
|
||||
period = "1"
|
||||
}
|
||||
|
||||
rows, err := stmt.Query(period)
|
||||
checkError(err)
|
||||
|
||||
results := make([]models.Pageview, 0)
|
||||
|
@ -43,11 +49,17 @@ var GetPageviewsDayCountHandler = http.HandlerFunc(func(w http.ResponseWriter, r
|
|||
stmt, err := core.DB.Prepare(`SELECT
|
||||
COUNT(*) AS count, DATE_FORMAT(timestamp, '%Y-%m-%d') AS date_group
|
||||
FROM visits
|
||||
WHERE timestamp >= DATE_SUB(CURRENT_TIMESTAMP, INTERVAL ? DAY)
|
||||
GROUP BY date_group`)
|
||||
checkError(err)
|
||||
defer stmt.Close()
|
||||
|
||||
rows, err := stmt.Query()
|
||||
period := r.URL.Query().Get("period")
|
||||
if period == "" {
|
||||
period = "1"
|
||||
}
|
||||
|
||||
rows, err := stmt.Query(period)
|
||||
checkError(err)
|
||||
|
||||
type Datapoint struct {
|
||||
|
|
|
@ -65,11 +65,17 @@ var GetVisitsDayCountHandler = http.HandlerFunc(func(w http.ResponseWriter, r *h
|
|||
stmt, err := core.DB.Prepare(`SELECT
|
||||
COUNT(DISTINCT(ip_address)) AS count, DATE_FORMAT(timestamp, '%Y-%m-%d') AS date_group
|
||||
FROM visits
|
||||
WHERE timestamp >= DATE_SUB(CURRENT_TIMESTAMP, INTERVAL ? DAY)
|
||||
GROUP BY date_group`)
|
||||
checkError(err)
|
||||
defer stmt.Close()
|
||||
|
||||
rows, err := stmt.Query()
|
||||
period := r.URL.Query().Get("period")
|
||||
if period == "" {
|
||||
period = "1"
|
||||
}
|
||||
|
||||
rows, err := stmt.Query(period)
|
||||
checkError(err)
|
||||
|
||||
type Datapoint struct {
|
||||
|
|
|
@ -0,0 +1,50 @@
|
|||
'use strict';
|
||||
|
||||
import { h, render, Component } from 'preact';
|
||||
|
||||
class DatePicker extends Component {
|
||||
constructor(props) {
|
||||
super(props)
|
||||
|
||||
this.state = {
|
||||
period: this.props.period
|
||||
}
|
||||
|
||||
this.periods = [
|
||||
{
|
||||
id: 7,
|
||||
label: 'Last 7 days'
|
||||
},
|
||||
{
|
||||
id: 30,
|
||||
label: 'Last 30 days'
|
||||
},
|
||||
{
|
||||
id: 90,
|
||||
label: 'Last quarter'
|
||||
}
|
||||
]
|
||||
this.setPeriod = this.setPeriod.bind(this)
|
||||
}
|
||||
|
||||
setPeriod(e) {
|
||||
this.setState({ period: parseInt(e.target.value) })
|
||||
this.props.onChoose(this.state.period);
|
||||
}
|
||||
|
||||
render() {
|
||||
const buttons = this.periods.map((p) => {
|
||||
let className = ( p.id == this.state.period ) ? 'active' : '';
|
||||
return <button value={p.id} class={className} onClick={this.setPeriod}>{p.label}</button>
|
||||
});
|
||||
|
||||
return (
|
||||
|
||||
<div class="small-margin">
|
||||
{buttons}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
export default DatePicker
|
|
@ -16,7 +16,13 @@ class Graph extends Component {
|
|||
pageviewData: []
|
||||
}
|
||||
this.fetchData = this.fetchData.bind(this);
|
||||
this.fetchData();
|
||||
this.fetchData(props.period);
|
||||
}
|
||||
|
||||
componentWillReceiveProps(newProps) {
|
||||
if(this.props.period != newProps.period) {
|
||||
this.fetchData(newProps.period)
|
||||
}
|
||||
}
|
||||
|
||||
refreshChart() {
|
||||
|
@ -47,9 +53,9 @@ class Graph extends Component {
|
|||
});
|
||||
}
|
||||
|
||||
fetchData() {
|
||||
fetchData(period) {
|
||||
// fetch visitor data
|
||||
fetch('/api/visits/count/day', {
|
||||
fetch('/api/visits/count/day?period=' + period, {
|
||||
credentials: 'include'
|
||||
}).then((r) => r.json())
|
||||
.then((data) => {
|
||||
|
@ -58,7 +64,7 @@ class Graph extends Component {
|
|||
});
|
||||
|
||||
// fetch pageview data
|
||||
fetch('/api/pageviews/count/day', {
|
||||
fetch('/api/pageviews/count/day?period=' + period, {
|
||||
credentials: 'include'
|
||||
}).then((r) => r.json())
|
||||
.then((data) => {
|
||||
|
|
|
@ -11,11 +11,17 @@ class Pageviews extends Component {
|
|||
records: []
|
||||
}
|
||||
this.fetchRecords = this.fetchRecords.bind(this);
|
||||
this.fetchRecords();
|
||||
this.fetchRecords(props.period);
|
||||
}
|
||||
|
||||
fetchRecords() {
|
||||
return fetch('/api/pageviews', {
|
||||
componentWillReceiveProps(newProps) {
|
||||
if(this.props.period != newProps.period) {
|
||||
this.fetchRecords(newProps.period)
|
||||
}
|
||||
}
|
||||
|
||||
fetchRecords(period) {
|
||||
return fetch('/api/pageviews?period=' + period, {
|
||||
credentials: 'include'
|
||||
}).then((r) => {
|
||||
if( r.ok ) {
|
||||
|
|
|
@ -6,13 +6,15 @@ import LogoutButton from './components/LogoutButton.js';
|
|||
import Pageviews from './components/Pageviews.js';
|
||||
import Realtime from './components/Realtime.js';
|
||||
import Graph from './components/Graph.js';
|
||||
import DatePicker from './components/DatePicker.js';
|
||||
|
||||
class App extends Component {
|
||||
constructor(props) {
|
||||
super(props)
|
||||
|
||||
this.state = {
|
||||
this.state = window.state = {
|
||||
authenticated: document.cookie.indexOf('auth') > -1,
|
||||
period: 7
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -29,8 +31,9 @@ class App extends Component {
|
|||
</div>
|
||||
</header>
|
||||
<Realtime />
|
||||
<Graph />
|
||||
<Pageviews />
|
||||
<DatePicker period={this.state.period} onChoose={(p) => { this.setState({ period: p })}} />
|
||||
<Graph period={this.state.period} />
|
||||
<Pageviews period={this.state.period} />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
|
|
@ -0,0 +1,27 @@
|
|||
.pull-right {
|
||||
float: right;
|
||||
}
|
||||
|
||||
.pull-left {
|
||||
float: left;
|
||||
}
|
||||
|
||||
.cf:before,
|
||||
.cf:after {
|
||||
content: " "; /* 1 */
|
||||
display: table; /* 2 */
|
||||
}
|
||||
|
||||
.cf:after {
|
||||
clear: both;
|
||||
}
|
||||
|
||||
.small-margin {
|
||||
margin-top: 20px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.medium-margin {
|
||||
margin-top: 40px;
|
||||
margin-bottom: 40px;
|
||||
}
|
|
@ -1,4 +1,5 @@
|
|||
@import "normalize";
|
||||
@import "util";
|
||||
|
||||
body {
|
||||
font-family: Arial, Verdana, sans-serif;
|
||||
|
@ -9,6 +10,7 @@ body {
|
|||
|
||||
h1, h2, h3 {
|
||||
color: #111;
|
||||
margin: 0;
|
||||
|
||||
small {
|
||||
font-weight: normal;
|
||||
|
@ -36,9 +38,15 @@ label {
|
|||
|
||||
input {
|
||||
padding: 8px;
|
||||
}
|
||||
|
||||
&[type="submit"] {
|
||||
cursor: pointer;
|
||||
button,
|
||||
input[type="submit"] {
|
||||
padding: 8px;
|
||||
cursor: pointer;
|
||||
|
||||
&.active {
|
||||
border: 2px solid black;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -47,6 +55,10 @@ input {
|
|||
padding: 0 20px;
|
||||
}
|
||||
|
||||
.header {
|
||||
margin: 20px 0;
|
||||
}
|
||||
|
||||
.block {
|
||||
width: auto;
|
||||
background: white;
|
||||
|
@ -54,37 +66,11 @@ input {
|
|||
margin-bottom: 20px;
|
||||
|
||||
h1, h2, h3 {
|
||||
margin-top: 0;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
}
|
||||
|
||||
.form-group {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.count {
|
||||
font-weight: bold;
|
||||
font-size: 120%;
|
||||
}
|
||||
|
||||
.header {
|
||||
line-height: 40px;
|
||||
}
|
||||
|
||||
.pull-right {
|
||||
float: right;
|
||||
}
|
||||
|
||||
.pull-left {
|
||||
float: left;
|
||||
}
|
||||
|
||||
.cf:before,
|
||||
.cf:after {
|
||||
content: " "; /* 1 */
|
||||
display: table; /* 2 */
|
||||
}
|
||||
|
||||
.cf:after {
|
||||
clear: both;
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue