Skip to content

Commit

Permalink
Add widget
Browse files Browse the repository at this point in the history
  • Loading branch information
MichaelBelgium committed Jun 26, 2024
1 parent 0d2b1de commit e0f344d
Show file tree
Hide file tree
Showing 8 changed files with 277 additions and 3 deletions.
3 changes: 2 additions & 1 deletion js/dist/admin.js

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions js/dist/admin.js.LICENSE.txt

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion js/dist/admin.js.map

Large diffs are not rendered by default.

95 changes: 95 additions & 0 deletions js/src/admin/components/Umami.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
import app from 'flarum/admin/app';

export default class Umami {
static instance = null;
bearerToken = null;

static getInstance() {
if (!Umami.instance) {
Umami.instance = new Umami();
}
return Umami.instance;
}

async login() {
if (this.bearerToken) {
console.info('[michaelbelgium-umami] Bearer token already set, not fetching new one.');
return true;
}

if (
app.data.settings['michaelbelgium-umami.api_username'] === undefined ||
app.data.settings['michaelbelgium-umami.api_password'] === undefined ||
app.data.settings['michaelbelgium-umami.api_username'] === '' ||
app.data.settings['michaelbelgium-umami.api_password'] === ''
) {
console.error('[michaelbelgium-umami] API username and/or password not set or emtpy.');
return false;
}

const corsProxyUrl = 'https://cors-anywhere.herokuapp.com/';
const targetUrl = app.data.settings['michaelbelgium-umami.domain'] + '/api/auth/login';

try {
const response = await app.request({
url: corsProxyUrl + targetUrl,
method: 'POST',
body: {
username: app.data.settings['michaelbelgium-umami.api_username'],
password: app.data.settings['michaelbelgium-umami.api_password'],
},
});

this.bearerToken = response.token;
console.info('[michaelbelgium-umami] Logged in as', response.user.username);
return true;
} catch (error) {
console.error('[michaelbelgium-umami] Error logging in:', error);
}
return false;
}

async website() {
return await app.request({
url: app.data.settings['michaelbelgium-umami.domain'] + '/api/websites/' + app.data.settings['michaelbelgium-umami.site_id'],
method: 'GET',
headers: {
Authorization: 'Bearer ' + this.bearerToken,
},
});
}

async websiteStats() {
let stats = {};

const now = new Date();
const weekAgo = new Date(now.getTime() - 7 * 24 * 60 * 60 * 1000);
weekAgo.setHours(0, 0, 0, 0);

let response = await app.request({
url: app.data.settings['michaelbelgium-umami.domain'] + '/api/websites/' + app.data.settings['michaelbelgium-umami.site_id'] + '/stats',
method: 'GET',
headers: {
Authorization: 'Bearer ' + this.bearerToken,
},
params: {
startAt: weekAgo.getTime(),
endAt: now.getTime(),
},
});

stats = response;

response = await app.request({
url: app.data.settings['michaelbelgium-umami.domain'] + '/api/websites/' + app.data.settings['michaelbelgium-umami.site_id'] + '/active',
method: 'GET',
headers: {
Authorization: 'Bearer ' + this.bearerToken,
},
});

stats.live = response.x;

return stats;
}
}
103 changes: 103 additions & 0 deletions js/src/admin/components/UmamiWidget.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
import DashboardWidget, { IDashboardWidgetAttrs } from 'flarum/admin/components/DashboardWidget';
import LoadingIndicator from 'flarum/common/components/LoadingIndicator';
import Umami from './Umami';
import icon from 'flarum/common/helpers/icon';
import app from 'flarum/admin/app';

export default class UmamiWidget extends DashboardWidget {
loading = false;
umami = Umami.getInstance();
stats = null;
website = null;
success = true;

async oninit(vnode) {
super.oninit(vnode);

this.loading = true;
m.redraw();

this.success = await this.umami.login();

if (this.success) {
this.stats = await this.umami.websiteStats();
this.website = await this.umami.website();
}

this.loading = false;
m.redraw();
}

className() {
return 'UmamiWidget';
}

content() {
if (!this.success) {
return (
<div>
<h4>Umami statistics</h4>
<p>{app.translator.trans('michaelbelgium-umami.admin.settings.errors.no_connection')}</p>
</div>
);
}

return (
<div>
<h4>Umami statistics</h4>
<div className="UmamiWidget-stats">
<section>
<dt>{this.loading ? <LoadingIndicator size="small" display="inline" /> : <>{this.stats.live}</>}</dt>
<h3>
{icon('fas fa-circle fa-beat')} {app.translator.trans('michaelbelgium-umami.admin.widget.live')}
</h3>
</section>
<section>
<dt>
{this.loading ? (
<LoadingIndicator size="small" display="inline" />
) : (
<>
{this.stats.pageviews.value} {this.changeToSpan(this.stats.visits.change)}
</>
)}
</dt>
<h3>{app.translator.trans('michaelbelgium-umami.admin.widget.views')}</h3>
</section>
<section>
<dt>
{this.loading ? (
<LoadingIndicator size="small" display="inline" />
) : (
<>
{this.stats.visits.value} {this.changeToSpan(this.stats.visits.value)}
</>
)}
</dt>
<h3>{app.translator.trans('michaelbelgium-umami.admin.widget.visits')}</h3>
</section>
<section>
<dt>
{this.loading ? (
<LoadingIndicator size="small" display="inline" />
) : (
<>
{this.stats.visitors.value} {this.changeToSpan(this.stats.visitors.change)}
</>
)}
</dt>
<h3>{app.translator.trans('michaelbelgium-umami.admin.widget.visitors')}</h3>
</section>
</div>
</div>
);
}

changeToSpan(change) {
return (
<span className={change >= 0 ? 'more' : 'less'}>
{change >= 0 ? '+' : '-'} {change}
</span>
);
}
}
17 changes: 17 additions & 0 deletions js/src/admin/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
import app from 'flarum/admin/app';
import { extend } from 'flarum/common/extend';
import DashboardPage from 'flarum/admin/components/DashboardPage';
import UmamiWidget from './components/UmamiWidget';

app.initializers.add('michaelbelgium/flarum-umami', () => {
app.extensionData
Expand All @@ -16,5 +19,19 @@ app.initializers.add('michaelbelgium/flarum-umami', () => {
help: app.translator.trans('michaelbelgium-umami.admin.settings.domain.help'),
placeholder: 'https://umami.yourdomain.com',
type: 'text',
})
.registerSetting({
setting: 'michaelbelgium-umami.api_username',
label: app.translator.trans('michaelbelgium-umami.admin.settings.username.label'),
type: 'text',
})
.registerSetting({
setting: 'michaelbelgium-umami.api_password',
label: app.translator.trans('michaelbelgium-umami.admin.settings.password.label'),
type: 'password',
});

extend(DashboardPage.prototype, 'availableWidgets', function (widgets) {
widgets.add('umami', UmamiWidget.component());
});
});
46 changes: 46 additions & 0 deletions less/admin.less
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
.DashboardPage .UmamiWidget {
h4 {
color: @muted-color;
margin-top: 0;
}

.UmamiWidget-stats {
display: flex;
justify-content: space-between;
width: 25%;

section {
dt {
color: #111;
font-size: 20px;
font-weight: bold;
text-align: right;

span {
font-size: 12px;
padding: 0 5px;

&.more {
color: #15a46e;
background: #cef8e0;
}

&.less {
color: #f75c46;
background: #ffebe7;
}
}
}

h3 {
font-size: 12px;
color: @muted-color;
text-transform: uppercase;

i.fa-circle {
color: green;
}
}
}
}
}
13 changes: 12 additions & 1 deletion locale/en.yml
Original file line number Diff line number Diff line change
@@ -1,9 +1,20 @@
michaelbelgium-umami:
admin:
widget:
live: 'Live'
views: 'Page views'
visits: 'Visits'
visitors: 'Visitors'
settings:
errors:
no_connection: 'Could not fetch statistics, please check your API credentials.'
site_id:
label: 'Website ID'
help: 'The website ID of this Flarum instance on Umami. You can find this in the details of the website settings on Umami.'
domain:
label: 'Domain'
help: 'The domain where Umami is located.'
help: 'The domain where Umami is located.'
username:
label: 'API Username'
password:
label: 'API Password'

0 comments on commit e0f344d

Please sign in to comment.