admin: migrate login stats chart to web component
This commit is contained in:
parent
582dfface9
commit
a2c7921c1f
|
@ -17,8 +17,8 @@
|
||||||
<i class="pf-icon pf-icon-server"></i> {% trans 'Logins over the last 24 hours' %}
|
<i class="pf-icon pf-icon-server"></i> {% trans 'Logins over the last 24 hours' %}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="pf-c-card__body" style="position: relative; height:100%; width:100%">
|
<div class="pf-c-card__body">
|
||||||
<canvas id="logins-last-metrics"></canvas>
|
<pb-admin-logins-chart url="{% url 'passbook_api:admin_metrics-list' %}"></pb-admin-logins-chart>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="pf-c-card pf-c-card-aggregate pf-l-gallery__item pf-m-4-col" style="grid-column-end: span 2;grid-row-end: span 3;">
|
<div class="pf-c-card pf-c-card-aggregate pf-l-gallery__item pf-m-4-col" style="grid-column-end: span 2;grid-row-end: span 3;">
|
||||||
|
@ -277,59 +277,4 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<script>
|
|
||||||
var ctx = document.getElementById('logins-last-metrics').getContext('2d');
|
|
||||||
fetch("{% url 'passbook_api:admin_metrics-list' %}").then(r => r.json()).then(r => {
|
|
||||||
var myChart = new Chart(ctx, {
|
|
||||||
type: 'bar',
|
|
||||||
data: {
|
|
||||||
datasets: [
|
|
||||||
{
|
|
||||||
label: 'Failed Logins',
|
|
||||||
backgroundColor: "rgba(201, 25, 11, .5)",
|
|
||||||
spanGaps: true,
|
|
||||||
data: r.logins_failed_per_1h,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: 'Successful Logins',
|
|
||||||
backgroundColor: "rgba(189, 229, 184, .5)",
|
|
||||||
spanGaps: true,
|
|
||||||
data: r.logins_per_1h,
|
|
||||||
},
|
|
||||||
]
|
|
||||||
},
|
|
||||||
options: {
|
|
||||||
maintainAspectRatio: false,
|
|
||||||
spanGaps: true,
|
|
||||||
scales: {
|
|
||||||
xAxes: [{
|
|
||||||
stacked: true,
|
|
||||||
gridLines: {
|
|
||||||
color: "rgba(0, 0, 0, 0)",
|
|
||||||
},
|
|
||||||
type: 'time',
|
|
||||||
offset: true,
|
|
||||||
ticks: {
|
|
||||||
callback: function (value, index, values) {
|
|
||||||
const date = new Date();
|
|
||||||
const delta = (date - values[index].value);
|
|
||||||
const ago = Math.round(delta / 1000 / 3600);
|
|
||||||
return `${ago} Hours ago`;
|
|
||||||
},
|
|
||||||
autoSkip: true,
|
|
||||||
maxTicksLimit: 8
|
|
||||||
}
|
|
||||||
}],
|
|
||||||
yAxes: [{
|
|
||||||
stacked: true,
|
|
||||||
gridLines: {
|
|
||||||
color: "rgba(0, 0, 0, 0)",
|
|
||||||
}
|
|
||||||
}]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
</script>
|
|
||||||
|
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
|
@ -15,7 +15,6 @@
|
||||||
<script src="{% static 'node_modules/codemirror/mode/xml/xml.js' %}"></script>
|
<script src="{% static 'node_modules/codemirror/mode/xml/xml.js' %}"></script>
|
||||||
<script src="{% static 'node_modules/codemirror/mode/yaml/yaml.js' %}"></script>
|
<script src="{% static 'node_modules/codemirror/mode/yaml/yaml.js' %}"></script>
|
||||||
<script src="{% static 'node_modules/codemirror/mode/python/python.js' %}"></script>
|
<script src="{% static 'node_modules/codemirror/mode/python/python.js' %}"></script>
|
||||||
<script src="{% static 'node_modules/chart.js/dist/Chart.bundle.min.js' %}"></script>
|
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
{% block page_content %}
|
{% block page_content %}
|
||||||
|
|
|
@ -17,7 +17,7 @@
|
||||||
<link rel="stylesheet" type="text/css" href="{% static 'node_modules/@patternfly/patternfly/patternfly-addons.css' %}">
|
<link rel="stylesheet" type="text/css" href="{% static 'node_modules/@patternfly/patternfly/patternfly-addons.css' %}">
|
||||||
<link rel="stylesheet" type="text/css" href="{% static 'node_modules/@fortawesome/fontawesome-free/css/fontawesome.min.css' %}">
|
<link rel="stylesheet" type="text/css" href="{% static 'node_modules/@fortawesome/fontawesome-free/css/fontawesome.min.css' %}">
|
||||||
<link rel="stylesheet" type="text/css" href="{% static 'passbook/passbook.css' %}">
|
<link rel="stylesheet" type="text/css" href="{% static 'passbook/passbook.css' %}">
|
||||||
<script src="{% static 'passbook/main.js' %}" defer></script>
|
<script src="{% static 'passbook/main.js' %}" type="module"></script>
|
||||||
{% block head %}
|
{% block head %}
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
</head>
|
</head>
|
||||||
|
|
8
passbook/static/static/package-lock.json
generated
8
passbook/static/static/package-lock.json
generated
|
@ -73,6 +73,14 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"@types/chart.js": {
|
||||||
|
"version": "2.9.28",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/chart.js/-/chart.js-2.9.28.tgz",
|
||||||
|
"integrity": "sha512-9YYhsxRngRJb0dkuaU5BezkF+zvvVHnwdRw+rtlahtFb4zqNf9YSgWsOq+dLYeh0fqsWmHUYLR64eNigh02F+w==",
|
||||||
|
"requires": {
|
||||||
|
"moment": "^2.10.2"
|
||||||
|
}
|
||||||
|
},
|
||||||
"@types/clean-css": {
|
"@types/clean-css": {
|
||||||
"version": "4.2.2",
|
"version": "4.2.2",
|
||||||
"resolved": "https://registry.npmjs.org/@types/clean-css/-/clean-css-4.2.2.tgz",
|
"resolved": "https://registry.npmjs.org/@types/clean-css/-/clean-css-4.2.2.tgz",
|
||||||
|
|
|
@ -7,6 +7,7 @@
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@fortawesome/fontawesome-free": "^5.15.1",
|
"@fortawesome/fontawesome-free": "^5.15.1",
|
||||||
"@patternfly/patternfly": "^4.59.1",
|
"@patternfly/patternfly": "^4.59.1",
|
||||||
|
"@types/chart.js": "^2.9.28",
|
||||||
"chart.js": "^2.9.4",
|
"chart.js": "^2.9.4",
|
||||||
"codemirror": "^5.58.2",
|
"codemirror": "^5.58.2",
|
||||||
"lit-element": "^2.4.0",
|
"lit-element": "^2.4.0",
|
||||||
|
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
@ -9,12 +9,11 @@ export default [{
|
||||||
input: './src/main.ts',
|
input: './src/main.ts',
|
||||||
output: [
|
output: [
|
||||||
{
|
{
|
||||||
format: 'iife',
|
format: 'es',
|
||||||
dir: 'passbook',
|
dir: 'passbook',
|
||||||
sourcemap: true
|
sourcemap: true
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
|
||||||
plugins: [
|
plugins: [
|
||||||
typescript(),
|
typescript(),
|
||||||
resolve({browser: true}),
|
resolve({browser: true}),
|
||||||
|
|
101
passbook/static/static/src/AdminLoginsChart.ts
Normal file
101
passbook/static/static/src/AdminLoginsChart.ts
Normal file
|
@ -0,0 +1,101 @@
|
||||||
|
import { css, customElement, html, LitElement, property } from "lit-element";
|
||||||
|
import Chart from "chart.js";
|
||||||
|
|
||||||
|
interface TickValue {
|
||||||
|
value: number;
|
||||||
|
major: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
@customElement("pb-admin-logins-chart")
|
||||||
|
export class AdminLoginsChart extends LitElement {
|
||||||
|
|
||||||
|
@property()
|
||||||
|
url: string = "";
|
||||||
|
|
||||||
|
chart: any;
|
||||||
|
|
||||||
|
static get styles() {
|
||||||
|
return css`
|
||||||
|
:host {
|
||||||
|
position: relative;
|
||||||
|
height: 100%;
|
||||||
|
width: 100%;
|
||||||
|
display: block;
|
||||||
|
min-height: 25rem;
|
||||||
|
}
|
||||||
|
canvas {
|
||||||
|
width: 100px;
|
||||||
|
height: 100px;
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
super();
|
||||||
|
window.addEventListener('resize', () => {
|
||||||
|
if (this.chart) {
|
||||||
|
this.chart.resize();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
firstUpdated() {
|
||||||
|
fetch(this.url).then(r => r.json()).catch(e => console.error(e)).then(r => {
|
||||||
|
let ctx = (<HTMLCanvasElement>this.shadowRoot?.querySelector("canvas")).getContext('2d')!;
|
||||||
|
this.chart = new Chart(ctx, {
|
||||||
|
type: 'bar',
|
||||||
|
data: {
|
||||||
|
datasets: [
|
||||||
|
{
|
||||||
|
label: 'Failed Logins',
|
||||||
|
backgroundColor: "rgba(201, 25, 11, .5)",
|
||||||
|
spanGaps: true,
|
||||||
|
data: r.logins_failed_per_1h,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Successful Logins',
|
||||||
|
backgroundColor: "rgba(189, 229, 184, .5)",
|
||||||
|
spanGaps: true,
|
||||||
|
data: r.logins_per_1h,
|
||||||
|
},
|
||||||
|
]
|
||||||
|
},
|
||||||
|
options: {
|
||||||
|
maintainAspectRatio: false,
|
||||||
|
spanGaps: true,
|
||||||
|
scales: {
|
||||||
|
xAxes: [{
|
||||||
|
stacked: true,
|
||||||
|
gridLines: {
|
||||||
|
color: "rgba(0, 0, 0, 0)",
|
||||||
|
},
|
||||||
|
type: 'time',
|
||||||
|
offset: true,
|
||||||
|
ticks: {
|
||||||
|
callback: function (value, index: number, values) {
|
||||||
|
const valueStamp = <TickValue>(<unknown>values[index]);
|
||||||
|
const delta = (Date.now() - valueStamp.value);
|
||||||
|
const ago = Math.round(delta / 1000 / 3600);
|
||||||
|
return `${ago} Hours ago`;
|
||||||
|
},
|
||||||
|
autoSkip: true,
|
||||||
|
maxTicksLimit: 8
|
||||||
|
}
|
||||||
|
}],
|
||||||
|
yAxes: [{
|
||||||
|
stacked: true,
|
||||||
|
gridLines: {
|
||||||
|
color: "rgba(0, 0, 0, 0)",
|
||||||
|
}
|
||||||
|
}]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
return html`<canvas></canvas>`;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -1,5 +1,6 @@
|
||||||
import "./legacy.js";
|
import "./legacy.js";
|
||||||
|
|
||||||
|
import "./AdminLoginsChart";
|
||||||
import './ActionButton';
|
import './ActionButton';
|
||||||
import './Dropdown';
|
import './Dropdown';
|
||||||
import './FetchFillSlot';
|
import './FetchFillSlot';
|
||||||
|
|
Reference in a new issue