From 019b7c97430f3f78aee907270c2cba7ca6d64ba7 Mon Sep 17 00:00:00 2001 From: Danny van Kooten Date: Tue, 20 Nov 2018 12:11:35 +0100 Subject: [PATCH] show pagination arrows in table component which allows paginating the table results. closes #153 --- assets/src/css/styles.css | 10 +++++++++ assets/src/css/util.css | 2 +- assets/src/js/components/Table.js | 28 ++++++++++++++++++++++-- gulpfile.js | 2 +- pkg/api/page_stats.go | 2 +- pkg/api/params.go | 8 +++++++ pkg/api/referrer_stats.go | 2 +- pkg/datastore/datastore.go | 4 ++-- pkg/datastore/sqlstore/page_stats.go | 6 ++--- pkg/datastore/sqlstore/referrer_stats.go | 6 ++--- 10 files changed, 56 insertions(+), 14 deletions(-) diff --git a/assets/src/css/styles.css b/assets/src/css/styles.css index b687c5d7..8ffe5fe3 100644 --- a/assets/src/css/styles.css +++ b/assets/src/css/styles.css @@ -84,6 +84,16 @@ nav .settings svg:hover { transform: rotate(45deg); } .table-row:after { content: ""; background: #88ffc6; position: absolute; height: 34px; top: 0; left: -16px; opacity: .2; border-right: 2px solid #45ce8c; z-index: 0; } .table-row.header:after { background: none; border: none; } +.row.pag { + margin-top: 16px; + grid-template-columns: 1fr 1fr; + grid-gap: 4px; +} +.row.pag a { + color: #98a0a6; + font-size: 19px; +} + .modal-wrap { position: fixed; height: 100%; width: 100%; top: 0; left: 0; z-index: 1977; background: rgba(20,20,20,.8); display: grid; grid-template-columns: 1fr; align-items: center; } .modal { max-width: 480px; width: 100%; margin: 0 auto; text-align: left; background: #fff; z-index: 1978; height: auto; border-radius: 4px; box-shadow: 0 2px 8px 0 rgba(34,34,34,.10); overflow: hidden; } .modal p { padding: 16px; font-size: 12px; color: #aaa; } diff --git a/assets/src/css/util.css b/assets/src/css/util.css index a0dbc7b9..fbd5faab 100644 --- a/assets/src/css/util.css +++ b/assets/src/css/util.css @@ -58,5 +58,5 @@ .fadeInDown { animation-name: fadeInDown; } .loading { - opacity: 0.8; + opacity: 0.6; } diff --git a/assets/src/js/components/Table.js b/assets/src/js/components/Table.js index 7c3fbf3f..82fbaeb4 100644 --- a/assets/src/js/components/Table.js +++ b/assets/src/js/components/Table.js @@ -5,6 +5,7 @@ import * as numbers from '../lib/numbers.js'; import Client from '../lib/client.js'; import { bind } from 'decko'; import classNames from 'classnames'; +import { runInNewContext } from 'vm'; const dayInSeconds = 60 * 60 * 24; @@ -15,6 +16,7 @@ class Table extends Component { this.state = { records: [], + offset: 0, limit: 15, loading: true, total: 0, @@ -37,7 +39,7 @@ class Table extends Component { fetchData(props) { this.setState({ loading: true }); - Client.request(`/sites/${props.siteId}/stats/${props.endpoint}/agg?before=${props.before}&after=${props.after}&limit=${this.state.limit}`) + Client.request(`/sites/${props.siteId}/stats/${props.endpoint}/agg?before=${props.before}&after=${props.after}&offset=${this.state.offset}&limit=${this.state.limit}`) .then((d) => { // request finished; check if timestamp range is still the one user wants to see if( this.paramsChanged(props, this.props) ) { @@ -57,12 +59,26 @@ class Table extends Component { total: d }); }); + } + + @bind + paginateNext() { + this.setState({ offset: this.state.offset + this.state.limit }) + this.fetchData(this.props) + } + + @bind + paginatePrev() { + if(this.state.offset == 0) { + return; + } + this.setState({ offset: Math.max(0, this.state.offset - this.state.limit) }) + this.fetchData(this.props) } render(props, state) { const tableRows = state.records !== null && state.records.length > 0 ? state.records.map((p, i) => { - let href = (p.Hostname + p.Pathname) || p.URL; let widthClass = ""; if(state.total > 0) { @@ -86,6 +102,13 @@ class Table extends Component { )}) :
Nothing here, yet.
; + // pagination row: only show when total # of results doesn't fit in one table page + const pagination = state.total > state.limit ? ( +
+ + +
) : ''; + return (
@@ -95,6 +118,7 @@ class Table extends Component {
{tableRows} + {pagination}
) diff --git a/gulpfile.js b/gulpfile.js index 5c33d5e2..d2a7cfd3 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -69,7 +69,7 @@ gulp.task('default', gulp.series('app-js', 'tracker-js', 'css', 'html', 'img', ' gulp.task('watch', gulp.series('default', function() { gulp.watch(['./assets/src/js/**/*.js'], gulp.parallel('app-js', 'tracker-js') ); - gulp.watch(['./assets/src/sass/**/**/*.scss'], gulp.parallel( 'css') ); + gulp.watch(['./assets/src/css/**/*.css'], gulp.parallel( 'css') ); gulp.watch(['./assets/src/**/*.html'], gulp.parallel( 'html') ); gulp.watch(['./assets/src/img/**/*'], gulp.parallel( 'img') ); gulp.watch(['./assets/src/fonts/**/*'], gulp.parallel( 'fonts') ); diff --git a/pkg/api/page_stats.go b/pkg/api/page_stats.go index 9712b89b..92ddb9f5 100644 --- a/pkg/api/page_stats.go +++ b/pkg/api/page_stats.go @@ -7,7 +7,7 @@ import ( // URL: /api/sites/{id:[0-9]+}/stats/pages/agg func (api *API) GetAggregatedPageStatsHandler(w http.ResponseWriter, r *http.Request) error { params := GetRequestParams(r) - result, err := api.database.SelectAggregatedPageStats(params.SiteID, params.StartDate, params.EndDate, params.Limit) + result, err := api.database.SelectAggregatedPageStats(params.SiteID, params.StartDate, params.EndDate, params.Offset, params.Limit) if err != nil { return err } diff --git a/pkg/api/params.go b/pkg/api/params.go index fd3138e8..8ae07334 100644 --- a/pkg/api/params.go +++ b/pkg/api/params.go @@ -12,6 +12,7 @@ import ( // Params defines the commonly used API parameters type Params struct { SiteID int64 + Offset int64 Limit int64 StartDate time.Time EndDate time.Time @@ -22,6 +23,7 @@ func GetRequestParams(r *http.Request) *Params { params := &Params{ SiteID: 0, Limit: 20, + Offset: 0, StartDate: time.Now(), EndDate: time.Now().AddDate(0, 0, -7), } @@ -52,6 +54,12 @@ func GetRequestParams(r *http.Request) *Params { } } + if q.Get("offset") != "" { + if offset, err := strconv.ParseInt(q.Get("offset"), 10, 64); err == nil && offset > 0 { + params.Offset = offset + } + } + return params } diff --git a/pkg/api/referrer_stats.go b/pkg/api/referrer_stats.go index 694ad186..aca3131e 100644 --- a/pkg/api/referrer_stats.go +++ b/pkg/api/referrer_stats.go @@ -6,7 +6,7 @@ import ( func (api *API) GetAggregatedReferrerStatsHandler(w http.ResponseWriter, r *http.Request) error { params := GetRequestParams(r) - result, err := api.database.SelectAggregatedReferrerStats(params.SiteID, params.StartDate, params.EndDate, params.Limit) + result, err := api.database.SelectAggregatedReferrerStats(params.SiteID, params.StartDate, params.EndDate, params.Offset, params.Limit) if err != nil { return err } diff --git a/pkg/datastore/datastore.go b/pkg/datastore/datastore.go index 42d50b8c..2353846c 100644 --- a/pkg/datastore/datastore.go +++ b/pkg/datastore/datastore.go @@ -42,13 +42,13 @@ type Datastore interface { // page stats GetPageStats(int64, time.Time, int64, int64) (*models.PageStats, error) SavePageStats(*models.PageStats) error - SelectAggregatedPageStats(int64, time.Time, time.Time, int64) ([]*models.PageStats, error) + SelectAggregatedPageStats(int64, time.Time, time.Time, int64, int64) ([]*models.PageStats, error) GetAggregatedPageStatsPageviews(int64, time.Time, time.Time) (int64, error) // referrer stats GetReferrerStats(int64, time.Time, int64, int64) (*models.ReferrerStats, error) SaveReferrerStats(*models.ReferrerStats) error - SelectAggregatedReferrerStats(int64, time.Time, time.Time, int64) ([]*models.ReferrerStats, error) + SelectAggregatedReferrerStats(int64, time.Time, time.Time, int64, int64) ([]*models.ReferrerStats, error) GetAggregatedReferrerStatsPageviews(int64, time.Time, time.Time) (int64, error) // hostnames diff --git a/pkg/datastore/sqlstore/page_stats.go b/pkg/datastore/sqlstore/page_stats.go index 277e8852..6415839a 100644 --- a/pkg/datastore/sqlstore/page_stats.go +++ b/pkg/datastore/sqlstore/page_stats.go @@ -38,7 +38,7 @@ func (db *sqlstore) updatePageStats(s *models.PageStats) error { return err } -func (db *sqlstore) SelectAggregatedPageStats(siteID int64, startDate time.Time, endDate time.Time, limit int64) ([]*models.PageStats, error) { +func (db *sqlstore) SelectAggregatedPageStats(siteID int64, startDate time.Time, endDate time.Time, offset int64, limit int64) ([]*models.PageStats, error) { var result []*models.PageStats query := db.Rebind(`SELECT h.name AS hostname, @@ -53,8 +53,8 @@ func (db *sqlstore) SelectAggregatedPageStats(siteID int64, startDate time.Time, LEFT JOIN pathnames p ON p.id = s.pathname_id WHERE site_id = ? AND ts >= ? AND ts <= ? GROUP BY hostname, pathname - ORDER BY pageviews DESC LIMIT ?`) - err := db.Select(&result, query, siteID, startDate.Format(DATE_FORMAT), endDate.Format(DATE_FORMAT), limit) + ORDER BY pageviews DESC LIMIT ?, ?`) + err := db.Select(&result, query, siteID, startDate.Format(DATE_FORMAT), endDate.Format(DATE_FORMAT), offset, limit) return result, err } diff --git a/pkg/datastore/sqlstore/referrer_stats.go b/pkg/datastore/sqlstore/referrer_stats.go index 11127ff2..9f20b1c3 100644 --- a/pkg/datastore/sqlstore/referrer_stats.go +++ b/pkg/datastore/sqlstore/referrer_stats.go @@ -38,7 +38,7 @@ func (db *sqlstore) updateReferrerStats(s *models.ReferrerStats) error { return err } -func (db *sqlstore) SelectAggregatedReferrerStats(siteID int64, startDate time.Time, endDate time.Time, limit int64) ([]*models.ReferrerStats, error) { +func (db *sqlstore) SelectAggregatedReferrerStats(siteID int64, startDate time.Time, endDate time.Time, offset int64, limit int64) ([]*models.ReferrerStats, error) { var result []*models.ReferrerStats sql := `SELECT @@ -59,11 +59,11 @@ func (db *sqlstore) SelectAggregatedReferrerStats(siteID int64, startDate time.T } else { sql = sql + `GROUP BY COALESCE(NULLIF(groupname, ''), CONCAT(hostname_id, pathname_id) ) ` } - sql = sql + ` ORDER BY pageviews DESC LIMIT ?` + sql = sql + ` ORDER BY pageviews DESC LIMIT ?, ?` query := db.Rebind(sql) - err := db.Select(&result, query, siteID, startDate.Format(DATE_FORMAT), endDate.Format(DATE_FORMAT), limit) + err := db.Select(&result, query, siteID, startDate.Format(DATE_FORMAT), endDate.Format(DATE_FORMAT), offset, limit) return result, mapError(err) }