mirror of
https://github.com/status-im/fathom.git
synced 2025-02-28 19:10:36 +00:00
add loading indicator to most widgets
This commit is contained in:
parent
c79e8407a0
commit
f304b082a5
@ -16,9 +16,8 @@ var GetPageviewsHandler = http.HandlerFunc(func(w http.ResponseWriter, r *http.R
|
||||
p.hostname,
|
||||
p.path,
|
||||
COUNT(*) AS pageviews,
|
||||
COUNT(DISTINCT(v.id)) AS pageviews_unique
|
||||
COUNT(DISTINCT(pv.visitor_id)) AS pageviews_unique
|
||||
FROM pageviews pv
|
||||
LEFT JOIN visitors v ON v.id = pv.visitor_id
|
||||
LEFT JOIN pages p ON pv.page_id = p.id
|
||||
WHERE UNIX_TIMESTAMP(pv.timestamp) <= ? AND UNIX_TIMESTAMP(pv.timestamp) >= ?
|
||||
GROUP BY p.path, p.hostname
|
||||
|
@ -29,8 +29,6 @@ var GetScreenResolutionsHandler = http.HandlerFunc(func(w http.ResponseWriter, r
|
||||
FROM pageviews pv
|
||||
LEFT JOIN visitors v ON v.id = pv.visitor_id
|
||||
WHERE UNIX_TIMESTAMP(pv.timestamp) <= ? AND UNIX_TIMESTAMP(pv.timestamp) >= ?
|
||||
AND v.screen_resolution IS NOT NULL
|
||||
AND v.screen_resolution != ""
|
||||
GROUP BY v.screen_resolution
|
||||
ORDER BY count DESC
|
||||
LIMIT ?`)
|
||||
|
@ -11,7 +11,8 @@ class CountWidget extends Component {
|
||||
|
||||
this.state = {
|
||||
count: 0,
|
||||
previousCount: 0
|
||||
previousCount: 0,
|
||||
loading: false
|
||||
}
|
||||
|
||||
this.fetchData = this.fetchData.bind(this);
|
||||
@ -27,9 +28,10 @@ class CountWidget extends Component {
|
||||
fetchData(period) {
|
||||
const before = Math.round((+new Date() ) / 1000);
|
||||
const after = before - ( period * dayInSeconds );
|
||||
this.setState({ loading: true })
|
||||
|
||||
Client.request(`${this.props.endpoint}/count?before=${before}&after=${after}`)
|
||||
.then((d) => { this.setState({ count: d })})
|
||||
.then((d) => { this.setState({ loading: false, count: d })})
|
||||
|
||||
// query previous period
|
||||
const previousBefore = after;
|
||||
@ -50,8 +52,10 @@ class CountWidget extends Component {
|
||||
}
|
||||
|
||||
render() {
|
||||
const loadingOverlay = this.state.loading ? <div class="loading-overlay"><div></div></div> : '';
|
||||
return (
|
||||
<div class="block center-text">
|
||||
{loadingOverlay}
|
||||
<h4 class="">{this.props.title}</h4>
|
||||
<div class="big tiny-margin">{numbers.formatWithComma(this.state.count)} {this.renderPercentage()}</div>
|
||||
<div class="muted">last {this.props.period} days</div>
|
||||
|
@ -12,13 +12,14 @@ class Pageviews extends Component {
|
||||
super(props)
|
||||
|
||||
this.state = {
|
||||
records: []
|
||||
records: [],
|
||||
loading: false
|
||||
}
|
||||
this.fetchRecords = this.fetchRecords.bind(this);
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
this.fetchRecords(this.props.period);
|
||||
this.fetchRecords(this.props.period)
|
||||
}
|
||||
|
||||
componentWillReceiveProps(newProps) {
|
||||
@ -30,13 +31,15 @@ class Pageviews extends Component {
|
||||
fetchRecords(period) {
|
||||
const before = Math.round((+new Date() ) / 1000);
|
||||
const after = before - ( period * dayInSeconds );
|
||||
this.setState({ loading: true })
|
||||
|
||||
Client.request(`/pageviews?before=${before}&after=${after}`)
|
||||
.then((d) => { this.setState({ records: d })})
|
||||
.then((d) => { this.setState({ loading: false, records: d })})
|
||||
.catch((e) => { console.log(e) })
|
||||
}
|
||||
|
||||
render() {
|
||||
const loadingOverlay = this.state.loading ? <div class="loading-overlay"><div></div></div> : '';
|
||||
const tableRows = this.state.records.map( (p, i) => (
|
||||
<tr>
|
||||
<td class="muted">{i+1}</td>
|
||||
@ -48,6 +51,7 @@ class Pageviews extends Component {
|
||||
|
||||
return (
|
||||
<div class="block">
|
||||
{loadingOverlay}
|
||||
<h3>Pageviews</h3>
|
||||
<table class="table pageviews">
|
||||
<thead>
|
||||
|
@ -12,7 +12,8 @@ class Table extends Component {
|
||||
|
||||
this.state = {
|
||||
records: [],
|
||||
limit: 5
|
||||
limit: 5,
|
||||
loading: true
|
||||
}
|
||||
|
||||
this.tableHeaders = props.headers.map(heading => <th>{heading}</th>)
|
||||
@ -46,11 +47,12 @@ class Table extends Component {
|
||||
}
|
||||
|
||||
fetchRecords(period, limit) {
|
||||
this.setState({ loading: true });
|
||||
const before = Math.round((+new Date() ) / 1000);
|
||||
const after = before - ( period * dayInSeconds );
|
||||
|
||||
Client.request(`${this.props.endpoint}?before=${before}&after=${after}&limit=${limit}`)
|
||||
.then((d) => { this.setState({ records: d })})
|
||||
.then((d) => { this.setState({ loading: false, records: d })})
|
||||
.catch((e) => { console.log(e) })
|
||||
}
|
||||
|
||||
@ -64,8 +66,11 @@ class Table extends Component {
|
||||
</tr>
|
||||
));
|
||||
|
||||
const loadingOverlay = this.state.loading ? <div class="loading-overlay"><div></div></div> : '';
|
||||
|
||||
return (
|
||||
<div class="block">
|
||||
{loadingOverlay}
|
||||
<div class="clearfix">
|
||||
<h3 class="pull-left">{this.props.title}</h3>
|
||||
<div class="pull-right">
|
||||
|
@ -69,6 +69,7 @@ a {
|
||||
background: white;
|
||||
padding: 20px;
|
||||
margin-bottom: 20px;
|
||||
position: relative;
|
||||
|
||||
h1, h2, h3 {
|
||||
margin-bottom: 20px;
|
||||
@ -80,6 +81,37 @@ a {
|
||||
float: left;
|
||||
}
|
||||
|
||||
|
||||
.loading-overlay {
|
||||
position: absolute;
|
||||
background: rgba( 255, 255, 255, 0.9 );
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
z-index: 10;
|
||||
font-weight: bold;
|
||||
margin: -20px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
|
||||
div {
|
||||
margin: 50px;
|
||||
height: 28px;
|
||||
width: 28px;
|
||||
animation: rotate 0.8s infinite linear;
|
||||
border: 8px solid #AAA;
|
||||
border-right-color: transparent;
|
||||
border-radius: 50%;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
@keyframes rotate {
|
||||
0% { transform: rotate(0deg); }
|
||||
100% { transform: rotate(360deg); }
|
||||
}
|
||||
|
||||
.count {
|
||||
font-weight: bold;
|
||||
font-size: 120%;
|
||||
|
@ -1,11 +1,3 @@
|
||||
CREATE TABLE pageviews(
|
||||
`id` INTEGER UNSIGNED AUTO_INCREMENT PRIMARY KEY,
|
||||
`page_id` INTEGER UNSIGNED NOT NULL,
|
||||
`visitor_id` INTEGER UNSIGNED NOT NULL,
|
||||
`referrer_keyword` TEXT NULL,
|
||||
`referrer_url` TEXT NULL,
|
||||
`timestamp` TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
||||
);
|
||||
|
||||
CREATE TABLE visitors(
|
||||
`id` INTEGER UNSIGNED AUTO_INCREMENT PRIMARY KEY,
|
||||
@ -21,6 +13,18 @@ CREATE TABLE visitors(
|
||||
|
||||
ALTER TABLE visitors ADD UNIQUE(`visitor_key`);
|
||||
|
||||
CREATE TABLE pageviews(
|
||||
`id` INTEGER UNSIGNED AUTO_INCREMENT PRIMARY KEY,
|
||||
`page_id` INTEGER UNSIGNED NOT NULL,
|
||||
`visitor_id` INTEGER UNSIGNED NOT NULL,
|
||||
`referrer_keyword` TEXT NULL,
|
||||
`referrer_url` TEXT NULL,
|
||||
`timestamp` TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
||||
);
|
||||
|
||||
ALTER TABLE pageviews ADD FOREIGN KEY(`visitor_id`) REFERENCES visitors(`id`);
|
||||
CREATE INDEX pageview_timestamp ON pageviews(timestamp(11));
|
||||
|
||||
CREATE TABLE pages(
|
||||
`id` INTEGER UNSIGNED AUTO_INCREMENT PRIMARY KEY NOT NULL,
|
||||
`hostname` VARCHAR(63) NOT NULL,
|
||||
|
15
db/seed.go
15
db/seed.go
@ -91,6 +91,9 @@ func seedPages() []models.Page {
|
||||
func Seed(n int) {
|
||||
pages := seedPages()
|
||||
|
||||
stmtVisitor, _ := Conn.Prepare("SELECT v.id FROM visitors v WHERE v.visitor_key = ? LIMIT 1")
|
||||
defer stmtVisitor.Close()
|
||||
|
||||
// insert X random hits
|
||||
for i := 0; i < n; i++ {
|
||||
|
||||
@ -107,11 +110,9 @@ func Seed(n int) {
|
||||
ScreenResolution: randSliceElement(screenResolutions),
|
||||
Country: randomdata.Country(randomdata.TwoCharCountry),
|
||||
}
|
||||
visitor.GenerateKey()
|
||||
visitor.Key = visitor.GenerateKey()
|
||||
|
||||
stmt, _ := Conn.Prepare("SELECT v.id FROM visitors v WHERE v.visitor_key = ? LIMIT 1")
|
||||
defer stmt.Close()
|
||||
err := stmt.QueryRow(visitor.Key).Scan(&visitor.ID)
|
||||
err := stmtVisitor.QueryRow(visitor.Key).Scan(&visitor.ID)
|
||||
if err != nil {
|
||||
visitor.Save(Conn)
|
||||
}
|
||||
@ -127,12 +128,16 @@ func Seed(n int) {
|
||||
Timestamp: timestamp,
|
||||
}
|
||||
|
||||
Conn.Exec("START TRANSACTION")
|
||||
|
||||
// insert between 1-4 pageviews for this visitor
|
||||
for j := 0; j < randInt(1, 4); j++ {
|
||||
for j := 0; j <= randInt(1, 4); j++ {
|
||||
page := pages[randInt(0, len(pages))]
|
||||
pv.PageID = page.ID
|
||||
pv.Save(Conn)
|
||||
}
|
||||
|
||||
Conn.Exec("COMMIT")
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -56,6 +56,5 @@ func (v *Visitor) Save(conn *sql.DB) error {
|
||||
// GenerateKey generates the "unique" visitor key
|
||||
func( v *Visitor) GenerateKey() string {
|
||||
byteKey := md5.Sum([]byte(v.IpAddress + v.DeviceOS + v.BrowserName + v.ScreenResolution))
|
||||
v.Key = hex.EncodeToString(byteKey[:])
|
||||
return v.Key
|
||||
return hex.EncodeToString(byteKey[:])
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user