add period date picker

This commit is contained in:
Danny van Kooten 2016-11-23 21:29:54 +01:00
parent 11ad2d5da4
commit 5c39e1f1df
8 changed files with 138 additions and 42 deletions

View File

@ -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 {

View File

@ -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 {

View File

@ -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

View File

@ -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) => {

View File

@ -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 ) {

View File

@ -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>
)
}

27
assets/sass/_util.scss Normal file
View File

@ -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;
}

View File

@ -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;
}