mirror of
https://github.com/henrywhitaker3/Speedtest-Tracker.git
synced 2025-12-21 13:23:04 +01:00
Added General settings section & min value on widgets
This commit is contained in:
@@ -7,7 +7,8 @@ use App\Setting;
|
||||
use Cache;
|
||||
use Carbon\Carbon;
|
||||
|
||||
class SettingsHelper {
|
||||
class SettingsHelper
|
||||
{
|
||||
|
||||
/**
|
||||
* Get a Setting object by name
|
||||
@@ -138,6 +139,11 @@ class SettingsHelper {
|
||||
{
|
||||
return [
|
||||
'base' => SettingsHelper::getBase(),
|
||||
'widgets' => [
|
||||
'show_average' => (bool)SettingsHelper::get('show_average')->value,
|
||||
'show_max' => (bool)SettingsHelper::get('show_max')->value,
|
||||
'show_min' => (bool)SettingsHelper::get('show_min')->value,
|
||||
],
|
||||
'graphs' => [
|
||||
'download_upload_graph_enabled' => SettingsHelper::get('download_upload_graph_enabled'),
|
||||
'download_upload_graph_width' => SettingsHelper::get('download_upload_graph_width'),
|
||||
|
||||
@@ -122,20 +122,38 @@ class SpeedtestController extends Controller
|
||||
public function latest()
|
||||
{
|
||||
$data = SpeedtestHelper::latest();
|
||||
$avg = Speedtest::select(DB::raw('AVG(ping) as ping, AVG(download) as download, AVG(upload) as upload'))
|
||||
->where('failed', false)
|
||||
->get();
|
||||
$max = Speedtest::select(DB::raw('MAX(ping) as ping, MAX(download) as download, MAX(upload) as upload'))
|
||||
->where('failed', false)
|
||||
->get();
|
||||
|
||||
if($data) {
|
||||
return response()->json([
|
||||
$response = [
|
||||
'method' => 'get latest speedtest',
|
||||
'data' => $data,
|
||||
'average' => $avg[0],
|
||||
'max' => $max[0],
|
||||
], 200);
|
||||
];
|
||||
|
||||
if (SettingsHelper::get('show_average')) {
|
||||
$avg = Speedtest::select(DB::raw('AVG(ping) as ping, AVG(download) as download, AVG(upload) as upload'))
|
||||
->where('failed', false)
|
||||
->first()
|
||||
->toArray();
|
||||
$response['average'] = $avg;
|
||||
}
|
||||
|
||||
if (SettingsHelper::get('show_max')) {
|
||||
$max = Speedtest::select(DB::raw('MAX(ping) as ping, MAX(download) as download, MAX(upload) as upload'))
|
||||
->where('failed', false)
|
||||
->first()
|
||||
->toArray();
|
||||
$response['maximum'] = $max;
|
||||
}
|
||||
|
||||
if (SettingsHelper::get('show_average')) {
|
||||
$min = Speedtest::select(DB::raw('MIN(ping) as ping, MIN(download) as download, MIN(upload) as upload'))
|
||||
->where('failed', false)
|
||||
->first()
|
||||
->toArray();
|
||||
$response['minimum'] = $min;
|
||||
}
|
||||
|
||||
if ($data) {
|
||||
return response()->json($response, 200);
|
||||
} else {
|
||||
return response()->json([
|
||||
'method' => 'get latest speedtest',
|
||||
|
||||
@@ -0,0 +1,56 @@
|
||||
<?php
|
||||
|
||||
use App\Helpers\SettingsHelper;
|
||||
use App\Setting;
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
class AddWidgetCardSettings extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
if (!SettingsHelper::get('show_average')) {
|
||||
Setting::create([
|
||||
'name' => 'show_average',
|
||||
'value' => true,
|
||||
'description' => 'If enabled, the average value for speedtests will be shown in the widgets.'
|
||||
]);
|
||||
}
|
||||
|
||||
if (!SettingsHelper::get('show_max')) {
|
||||
Setting::create([
|
||||
'name' => 'show_max',
|
||||
'value' => true,
|
||||
'description' => 'If enabled, the maximum value for speedtests will be shown in the widgets.'
|
||||
]);
|
||||
}
|
||||
|
||||
if (!SettingsHelper::get('show_min')) {
|
||||
Setting::create([
|
||||
'name' => 'show_min',
|
||||
'value' => true,
|
||||
'description' => 'If enabled, the minimum value for speedtests will be shown in the widgets.'
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
Setting::whereIn('name', [
|
||||
'show_average',
|
||||
'show_max',
|
||||
'show_min',
|
||||
])->delete();
|
||||
}
|
||||
}
|
||||
2
public/js/app.js
vendored
2
public/js/app.js
vendored
File diff suppressed because one or more lines are too long
@@ -131,9 +131,7 @@ export default class LatestResults extends Component {
|
||||
>
|
||||
<Widget
|
||||
title="Ping"
|
||||
value={parseFloat(data.data.ping).toFixed(1)}
|
||||
avg={parseFloat(data.average.ping).toFixed(1)}
|
||||
max={parseFloat(data.max.ping).toFixed(1)}
|
||||
data={data}
|
||||
failed={data.data.failed}
|
||||
unit="ms"
|
||||
icon="ping"
|
||||
@@ -147,9 +145,7 @@ export default class LatestResults extends Component {
|
||||
>
|
||||
<Widget
|
||||
title="Download"
|
||||
value={parseFloat(data.data.download).toFixed(1)}
|
||||
avg={parseFloat(data.average.download).toFixed(1)}
|
||||
max={parseFloat(data.max.download).toFixed(1)}
|
||||
data={data}
|
||||
failed={data.data.failed}
|
||||
unit="Mbit/s"
|
||||
icon="dl"
|
||||
@@ -163,9 +159,7 @@ export default class LatestResults extends Component {
|
||||
>
|
||||
<Widget
|
||||
title="Upload"
|
||||
value={parseFloat(data.data.upload).toFixed(1)}
|
||||
avg={parseFloat(data.average.upload).toFixed(1)}
|
||||
max={parseFloat(data.max.upload).toFixed(1)}
|
||||
data={data}
|
||||
failed={data.data.failed}
|
||||
unit="Mbit/s"
|
||||
icon="ul"
|
||||
|
||||
89
resources/js/components/Graphics/Widget.js
vendored
89
resources/js/components/Graphics/Widget.js
vendored
@@ -8,38 +8,88 @@ export default class Widget extends Component {
|
||||
|
||||
this.state = {
|
||||
title: this.props.title,
|
||||
value: this.props.value,
|
||||
unit: this.props.unit,
|
||||
icon: this.props.icon,
|
||||
avg: this.props.avg,
|
||||
max: this.props.max,
|
||||
failed: this.props.failed,
|
||||
data: this.props.data
|
||||
}
|
||||
}
|
||||
|
||||
parseData(title, data) {
|
||||
var returnData = {};
|
||||
|
||||
|
||||
if(title == 'Ping') {
|
||||
returnData.value = parseFloat(data.data.ping).toFixed(1);
|
||||
|
||||
if(window.config.widgets.show_average) {
|
||||
returnData.avg = parseFloat(data.average.ping).toFixed(1);
|
||||
}
|
||||
|
||||
if(window.config.widgets.show_max) {
|
||||
returnData.max = parseFloat(data.maximum.ping).toFixed(1);
|
||||
}
|
||||
|
||||
if(window.config.widgets.show_max) {
|
||||
returnData.min = parseFloat(data.minimum.ping).toFixed(1);
|
||||
}
|
||||
}
|
||||
|
||||
if(title == 'Upload') {
|
||||
returnData.value = parseFloat(data.data.upload).toFixed(1);
|
||||
|
||||
if(window.config.widgets.show_average) {
|
||||
returnData.avg = parseFloat(data.average.upload).toFixed(1);
|
||||
}
|
||||
|
||||
if(window.config.widgets.show_max) {
|
||||
returnData.max = parseFloat(data.maximum.upload).toFixed(1);
|
||||
}
|
||||
|
||||
if(window.config.widgets.show_max) {
|
||||
returnData.min = parseFloat(data.minimum.upload).toFixed(1);
|
||||
}
|
||||
}
|
||||
|
||||
if(title == 'Download') {
|
||||
returnData.value = parseFloat(data.data.download).toFixed(1);
|
||||
|
||||
if(window.config.widgets.show_average) {
|
||||
returnData.avg = parseFloat(data.average.download).toFixed(1);
|
||||
}
|
||||
|
||||
if(window.config.widgets.show_max) {
|
||||
returnData.max = parseFloat(data.maximum.download).toFixed(1);
|
||||
}
|
||||
|
||||
if(window.config.widgets.show_max) {
|
||||
returnData.min = parseFloat(data.minimum.download).toFixed(1);
|
||||
}
|
||||
}
|
||||
|
||||
return returnData;
|
||||
}
|
||||
|
||||
componentDidUpdate = () => {
|
||||
if(this.props.title != this.state.title || this.props.value != this.state.value || this.props.unit != this.state.unit || this.props.icon != this.state.icon || this.props.avg != this.state.avg || this.props.max != this.state.max || this.props.failed != this.state.failed) {
|
||||
if(this.props.title != this.state.title || this.props.data != this.state.data || this.props.unit != this.state.unit || this.props.icon != this.state.icon || this.props.failed != this.state.failed) {
|
||||
this.setState({
|
||||
title: this.props.title,
|
||||
value: this.props.value,
|
||||
unit: this.props.unit,
|
||||
icon: this.props.icon,
|
||||
avg: this.props.avg,
|
||||
max: this.props.max,
|
||||
failed: this.props.failed,
|
||||
data: this.props.data
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
var title = this.state.title;
|
||||
var value = this.state.value;
|
||||
var unit = this.state.unit;
|
||||
var icon = this.state.icon;
|
||||
var max = this.state.max;
|
||||
var avg = this.state.avg;
|
||||
var failed = Boolean(Number(this.state.failed));
|
||||
|
||||
var data = this.parseData(title, this.state.data);
|
||||
|
||||
switch(icon) {
|
||||
case 'ping':
|
||||
icon = <span className="ti-pulse icon text-success"></span>;
|
||||
@@ -63,17 +113,30 @@ export default class Widget extends Component {
|
||||
</div>
|
||||
|
||||
<div className="text-truncate">
|
||||
<h3 className="d-inline">{(!failed) ? value : <span className="ti-close text-danger"></span> }</h3>
|
||||
<h3 className="d-inline">{(!failed) ? data.value : <span className="ti-close text-danger"></span> }</h3>
|
||||
<p className="d-inline ml-2">{unit} (current)</p>
|
||||
</div>
|
||||
|
||||
{window.config.widgets.show_average &&
|
||||
<div className="text-muted text-truncate">
|
||||
<h5 className="d-inline">{avg}</h5>
|
||||
<h5 className="d-inline">{data.avg}</h5>
|
||||
<p className="d-inline ml-2">{unit} (average)</p>
|
||||
</div>
|
||||
}
|
||||
|
||||
{window.config.widgets.show_max &&
|
||||
<div className="text-muted text-truncate">
|
||||
<h5 className="d-inline">{max}</h5>
|
||||
<h5 className="d-inline">{data.max}</h5>
|
||||
<p className="d-inline ml-2">{unit} (maximum)</p>
|
||||
</div>
|
||||
}
|
||||
|
||||
{window.config.widgets.show_min &&
|
||||
<div className="text-muted text-truncate">
|
||||
<h5 className="d-inline">{data.min}</h5>
|
||||
<p className="d-inline ml-2">{unit} (minimum)</p>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
</Card.Body>
|
||||
|
||||
@@ -47,6 +47,10 @@ export default class SettingWithModal extends Component {
|
||||
if(this.state.autoClose) {
|
||||
this.toggleShow();
|
||||
}
|
||||
Axios.get('api/settings/config')
|
||||
.then((resp) => {
|
||||
window.config = resp.data;
|
||||
})
|
||||
})
|
||||
.catch((err) => {
|
||||
if(err.response.status == 422) {
|
||||
@@ -194,7 +198,7 @@ export default class SettingWithModal extends Component {
|
||||
</Col>
|
||||
{e.description == null &&
|
||||
<Col md={md} sm={sm}>
|
||||
<p>{e.obj.description}</p>
|
||||
<p dangerouslySetInnerHTML={{ __html: e.obj.description}}></p>
|
||||
</Col>
|
||||
}
|
||||
</Row>
|
||||
|
||||
26
resources/js/components/Settings/Settings.js
vendored
26
resources/js/components/Settings/Settings.js
vendored
@@ -59,10 +59,28 @@ export default class Settings extends Component {
|
||||
return (
|
||||
<Row>
|
||||
<Col lg={{ span: 4 }} md={{ span: 6 }} sm={{ span: 12 }}>
|
||||
<Setting name={e.schedule.name} value={e.schedule.value} description={e.schedule.description} />
|
||||
</Col>
|
||||
<Col lg={{ span: 4 }} md={{ span: 6 }} sm={{ span: 12 }}>
|
||||
<Setting name={e.server.name} value={e.server.value} description={e.server.description} />
|
||||
<SettingWithModal title="General settings" description="Configure general settings for the app." autoClose={true} settings={[
|
||||
{
|
||||
obj: e.schedule,
|
||||
type: 'text'
|
||||
},
|
||||
{
|
||||
obj: e.server,
|
||||
type: 'text'
|
||||
},
|
||||
{
|
||||
obj: e.show_average,
|
||||
type: 'checkbox'
|
||||
},
|
||||
{
|
||||
obj: e.show_max,
|
||||
type: 'checkbox'
|
||||
},
|
||||
{
|
||||
obj: e.show_min,
|
||||
type: 'checkbox'
|
||||
},
|
||||
]} />
|
||||
</Col>
|
||||
<Col lg={{ span: 4 }} md={{ span: 6 }} sm={{ span: 12 }}>
|
||||
<SettingWithModal title="Graph settings" description="Control settings for the graphs." autoClose={true} settings={[
|
||||
|
||||
Reference in New Issue
Block a user