mirror of
https://github.com/henrywhitaker3/Speedtest-Tracker.git
synced 2025-12-21 21:33:08 +01:00
52
app/Casts/CommaSeparatedArrayCast.php
Normal file
52
app/Casts/CommaSeparatedArrayCast.php
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Casts;
|
||||||
|
|
||||||
|
use Illuminate\Contracts\Database\Eloquent\CastsAttributes;
|
||||||
|
|
||||||
|
class CommaSeparatedArrayCast implements CastsAttributes
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Array of settings that should be cast
|
||||||
|
*/
|
||||||
|
private array $shouldCast = [
|
||||||
|
'visible_columns',
|
||||||
|
'hidden_columns',
|
||||||
|
];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Cast the given value.
|
||||||
|
*
|
||||||
|
* @param \Illuminate\Database\Eloquent\Model $model
|
||||||
|
* @param string $key
|
||||||
|
* @param mixed $value
|
||||||
|
* @param array $attributes
|
||||||
|
* @return mixed
|
||||||
|
*/
|
||||||
|
public function get($model, $key, $value, $attributes)
|
||||||
|
{
|
||||||
|
if (!in_array($model->name, $this->shouldCast)) {
|
||||||
|
return $value;
|
||||||
|
}
|
||||||
|
|
||||||
|
return explode(',', $value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Prepare the given value for storage.
|
||||||
|
*
|
||||||
|
* @param \Illuminate\Database\Eloquent\Model $model
|
||||||
|
* @param string $key
|
||||||
|
* @param mixed $value
|
||||||
|
* @param array $attributes
|
||||||
|
* @return mixed
|
||||||
|
*/
|
||||||
|
public function set($model, $key, $value, $attributes)
|
||||||
|
{
|
||||||
|
if (!in_array($model->name, $this->shouldCast)) {
|
||||||
|
return $value;
|
||||||
|
}
|
||||||
|
|
||||||
|
return implode(',', $value);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -168,6 +168,10 @@ class SettingsHelper
|
|||||||
'telegram_bot_token' => SettingsHelper::settingIsEditable('telegram_bot_token'),
|
'telegram_bot_token' => SettingsHelper::settingIsEditable('telegram_bot_token'),
|
||||||
'telegram_chat_id' => SettingsHelper::settingIsEditable('telegram_chat_id'),
|
'telegram_chat_id' => SettingsHelper::settingIsEditable('telegram_chat_id'),
|
||||||
],
|
],
|
||||||
|
'tables' => [
|
||||||
|
'visible_columns' => SettingsHelper::get('visible_columns')->value,
|
||||||
|
'hidden_columns' => SettingsHelper::get('hidden_columns')->value,
|
||||||
|
],
|
||||||
'auth' => (bool)SettingsHelper::get('auth')->value
|
'auth' => (bool)SettingsHelper::get('auth')->value
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
namespace App;
|
namespace App;
|
||||||
|
|
||||||
|
use App\Casts\CommaSeparatedArrayCast;
|
||||||
use App\Helpers\SettingsHelper;
|
use App\Helpers\SettingsHelper;
|
||||||
use Illuminate\Database\Eloquent\Model;
|
use Illuminate\Database\Eloquent\Model;
|
||||||
|
|
||||||
@@ -17,4 +18,8 @@ class Setting extends Model
|
|||||||
];
|
];
|
||||||
|
|
||||||
protected $table = 'settings';
|
protected $table = 'settings';
|
||||||
|
|
||||||
|
protected $casts = [
|
||||||
|
'value' => CommaSeparatedArrayCast::class,
|
||||||
|
];
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,9 @@
|
|||||||
{
|
{
|
||||||
"1.11.1": [
|
"1.11.1": [
|
||||||
|
{
|
||||||
|
"description": "Add option to show/hide columns in the all tests table.",
|
||||||
|
"link": ""
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"description": "Add option to delete failed tests.",
|
"description": "Add option to delete failed tests.",
|
||||||
"link": ""
|
"link": ""
|
||||||
|
|||||||
@@ -0,0 +1,38 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
use App\Helpers\SettingsHelper;
|
||||||
|
use App\Setting;
|
||||||
|
use Illuminate\Database\Migrations\Migration;
|
||||||
|
use Illuminate\Database\Schema\Blueprint;
|
||||||
|
use Illuminate\Support\Facades\Schema;
|
||||||
|
|
||||||
|
class AddVisibleColumnsSetting extends Migration
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Run the migrations.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function up()
|
||||||
|
{
|
||||||
|
if (!SettingsHelper::get('visible_columns')) {
|
||||||
|
Setting::create([
|
||||||
|
'name' => 'visible_columns',
|
||||||
|
'value' => 'id,created_at,download,upload,ping',
|
||||||
|
'description' => 'Choose and order the columns shown in the "All Tests" table.'
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reverse the migrations.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function down()
|
||||||
|
{
|
||||||
|
Setting::whereIn('name', [
|
||||||
|
'visible_columns',
|
||||||
|
])->delete();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
use App\Helpers\SettingsHelper;
|
||||||
|
use App\Setting;
|
||||||
|
use Illuminate\Database\Migrations\Migration;
|
||||||
|
use Illuminate\Database\Schema\Blueprint;
|
||||||
|
use Illuminate\Support\Facades\Schema;
|
||||||
|
|
||||||
|
class AddHiddenColumnsSetting extends Migration
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Run the migrations.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function up()
|
||||||
|
{
|
||||||
|
if (!SettingsHelper::get('hidden_columns')) {
|
||||||
|
Setting::create([
|
||||||
|
'name' => 'hidden_columns',
|
||||||
|
'value' => 'server_id,server_name,server_host,url,scheduled',
|
||||||
|
'description' => 'Columns hidden from the "All Tests" table.'
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reverse the migrations.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function down()
|
||||||
|
{
|
||||||
|
Setting::whereIn('name', [
|
||||||
|
'hidden_columns',
|
||||||
|
])->delete();
|
||||||
|
}
|
||||||
|
}
|
||||||
13287
package-lock.json
generated
13287
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -10,8 +10,8 @@
|
|||||||
"production": "cross-env NODE_ENV=production node_modules/webpack/bin/webpack.js --no-progress --hide-modules --config=node_modules/laravel-mix/setup/webpack.config.js"
|
"production": "cross-env NODE_ENV=production node_modules/webpack/bin/webpack.js --no-progress --hide-modules --config=node_modules/laravel-mix/setup/webpack.config.js"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"axios": "^0.21",
|
|
||||||
"@babel/preset-react": "^7.12.13",
|
"@babel/preset-react": "^7.12.13",
|
||||||
|
"axios": "^0.21",
|
||||||
"bootstrap": "^4.6.0",
|
"bootstrap": "^4.6.0",
|
||||||
"cross-env": "^7.0",
|
"cross-env": "^7.0",
|
||||||
"jquery": "^3.5",
|
"jquery": "^3.5",
|
||||||
@@ -29,6 +29,7 @@
|
|||||||
"chart.js": "^2.9.4",
|
"chart.js": "^2.9.4",
|
||||||
"csv-file-validator": "^1.10.1",
|
"csv-file-validator": "^1.10.1",
|
||||||
"js-cookie": "^2.2.1",
|
"js-cookie": "^2.2.1",
|
||||||
|
"react-beautiful-dnd": "^13.1.0",
|
||||||
"react-bootstrap": "^1.5.1",
|
"react-bootstrap": "^1.5.1",
|
||||||
"react-chartjs-2": "^2.11.1",
|
"react-chartjs-2": "^2.11.1",
|
||||||
"react-router": "^5.2.0",
|
"react-router": "^5.2.0",
|
||||||
|
|||||||
11759
public/js/app.js
vendored
11759
public/js/app.js
vendored
File diff suppressed because it is too large
Load Diff
72
resources/js/components/Graphics/TableRow.js
vendored
72
resources/js/components/Graphics/TableRow.js
vendored
@@ -47,18 +47,62 @@ export default class TableRow extends Component {
|
|||||||
this.toggleShow();
|
this.toggleShow();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getDataFields = () => {
|
||||||
|
let allFields = this.props.allFields;
|
||||||
|
let data = this.state.data;
|
||||||
|
let processedFields = [];
|
||||||
|
|
||||||
|
for(var key in allFields) {
|
||||||
|
let field = allFields[key];
|
||||||
|
|
||||||
|
let value = data[key];
|
||||||
|
|
||||||
|
if(field.type === 'date') {
|
||||||
|
value = new Date(value).toLocaleString();
|
||||||
|
} else if(field.type === 'bool') {
|
||||||
|
value = Boolean(value) ? field.if_true : field.if_false
|
||||||
|
}
|
||||||
|
|
||||||
|
let final = {
|
||||||
|
name: key,
|
||||||
|
key: field.alias,
|
||||||
|
value: value,
|
||||||
|
type: field.type
|
||||||
|
};
|
||||||
|
|
||||||
|
processedFields.push(final);
|
||||||
|
}
|
||||||
|
|
||||||
|
let visible = [];
|
||||||
|
let inModal = [];
|
||||||
|
|
||||||
|
window.config.tables.visible_columns.forEach(column => {
|
||||||
|
visible.push(processedFields.find(x => x.name == column));
|
||||||
|
});
|
||||||
|
|
||||||
|
inModal = processedFields.filter(el => {
|
||||||
|
return !visible.includes(el);
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
visible: visible,
|
||||||
|
modal: inModal
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
var e = this.state.data;
|
var e = this.state.data;
|
||||||
var show = this.state.show;
|
var show = this.state.show;
|
||||||
|
var fields = this.getDataFields();
|
||||||
|
|
||||||
if(e.failed != true) {
|
if(e.failed != true) {
|
||||||
return (
|
return (
|
||||||
<tr>
|
<tr>
|
||||||
<td>{e.id}</td>
|
{fields.visible.map((e, i) => {
|
||||||
<td>{new Date(e.created_at).toLocaleString()}</td>
|
return (
|
||||||
<td>{e.download}</td>
|
<td key={i}>{e.value}</td>
|
||||||
<td>{e.upload}</td>
|
);
|
||||||
<td>{e.ping}</td>
|
})}
|
||||||
{e.server_host != null ?
|
{e.server_host != null ?
|
||||||
<td>
|
<td>
|
||||||
<span onClick={this.toggleShow} className="ti-arrow-top-right mouse"></span>
|
<span onClick={this.toggleShow} className="ti-arrow-top-right mouse"></span>
|
||||||
@@ -67,13 +111,17 @@ export default class TableRow extends Component {
|
|||||||
<Modal.Title>More info</Modal.Title>
|
<Modal.Title>More info</Modal.Title>
|
||||||
</Modal.Header>
|
</Modal.Header>
|
||||||
<Modal.Body className="text-center">
|
<Modal.Body className="text-center">
|
||||||
<p>Server ID: {e.server_id}</p>
|
{fields.modal.map((e, i) => {
|
||||||
<p>Name: {e.server_name}</p>
|
if(e.type === 'url') {
|
||||||
<p>Host: {e.server_host}</p>
|
return (
|
||||||
<p>URL: <a href={e.url} target="_blank" rel="noopener noreferer">Speedtest.net</a></p>
|
<p key={i}>{e.key}: <a href={e.value} target="_blank" rel="noopener noreferer">Speedtest.net</a></p>
|
||||||
{e.scheduled != undefined &&
|
);
|
||||||
<p>Type: {e.scheduled == true ? 'scheduled' : 'manual'}</p>
|
} else {
|
||||||
}
|
return (
|
||||||
|
<p key={i}>{e.key}: {e.value}</p>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
})}
|
||||||
<Button variant="danger" onClick={() => { this.delete(e.id) }}>Delete</Button>
|
<Button variant="danger" onClick={() => { this.delete(e.id) }}>Delete</Button>
|
||||||
</Modal.Body>
|
</Modal.Body>
|
||||||
</Modal>
|
</Modal>
|
||||||
|
|||||||
59
resources/js/components/Graphics/TestsTable.js
vendored
59
resources/js/components/Graphics/TestsTable.js
vendored
@@ -14,7 +14,51 @@ export default class TestsTable extends Component {
|
|||||||
data: [],
|
data: [],
|
||||||
showTable: false,
|
showTable: false,
|
||||||
refresh: true,
|
refresh: true,
|
||||||
interval: null
|
interval: null,
|
||||||
|
allFields: {
|
||||||
|
id: {
|
||||||
|
type: 'int',
|
||||||
|
alias: 'ID'
|
||||||
|
},
|
||||||
|
created_at: {
|
||||||
|
type: 'date',
|
||||||
|
alias: 'Time'
|
||||||
|
},
|
||||||
|
download: {
|
||||||
|
type: 'float',
|
||||||
|
alias: 'Download (Mbit/s)'
|
||||||
|
},
|
||||||
|
upload: {
|
||||||
|
type: 'float',
|
||||||
|
alias: 'Upload (Mbit/s)'
|
||||||
|
},
|
||||||
|
ping: {
|
||||||
|
type: 'float',
|
||||||
|
alias: 'Ping (ms)'
|
||||||
|
},
|
||||||
|
server_id: {
|
||||||
|
type: 'int',
|
||||||
|
alias: 'Server ID'
|
||||||
|
},
|
||||||
|
server_name: {
|
||||||
|
type: 'string',
|
||||||
|
alias: 'Name'
|
||||||
|
},
|
||||||
|
server_host: {
|
||||||
|
type: 'string',
|
||||||
|
alias: 'Host'
|
||||||
|
},
|
||||||
|
url: {
|
||||||
|
type: 'url',
|
||||||
|
alias: 'URL'
|
||||||
|
},
|
||||||
|
scheduled: {
|
||||||
|
type: 'bool',
|
||||||
|
alias: 'Type',
|
||||||
|
if_true: 'scheduled',
|
||||||
|
if_false: 'manual'
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -84,6 +128,7 @@ export default class TestsTable extends Component {
|
|||||||
var data = this.state.data;
|
var data = this.state.data;
|
||||||
var show = this.state.showTable;
|
var show = this.state.showTable;
|
||||||
var refresh = this.state.refresh;
|
var refresh = this.state.refresh;
|
||||||
|
let allFields = this.state.allFields;
|
||||||
|
|
||||||
if(data.length > 0) {
|
if(data.length > 0) {
|
||||||
return (
|
return (
|
||||||
@@ -102,18 +147,18 @@ export default class TestsTable extends Component {
|
|||||||
<Table responsive>
|
<Table responsive>
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th>ID</th>
|
{window.config.tables.visible_columns.map((e, i) => {
|
||||||
<th>Time</th>
|
return (
|
||||||
<th>Download (Mbit/s)</th>
|
<th key={i}>{allFields[e].alias}</th>
|
||||||
<th>Upload (Mbit/s)</th>
|
);
|
||||||
<th>Ping (ms)</th>
|
})}
|
||||||
<th>More</th>
|
<th>More</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
{data.map((e,i) => {
|
{data.map((e,i) => {
|
||||||
return (
|
return (
|
||||||
<TableRow key={e.id} data={e} refresh={this.getData} />
|
<TableRow key={e.id} data={e} allFields={allFields} refresh={this.getData} />
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
</tbody>
|
</tbody>
|
||||||
|
|||||||
@@ -63,6 +63,16 @@ export default class SettingsIndex extends Component {
|
|||||||
type: 'checkbox',
|
type: 'checkbox',
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
Tables: [
|
||||||
|
{
|
||||||
|
obj: data.visible_columns,
|
||||||
|
type: 'list'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
obj: data.hidden_columns,
|
||||||
|
type: 'list'
|
||||||
|
}
|
||||||
|
],
|
||||||
Graphs: [
|
Graphs: [
|
||||||
{
|
{
|
||||||
obj: data.download_upload_graph_enabled,
|
obj: data.download_upload_graph_enabled,
|
||||||
@@ -268,7 +278,7 @@ export default class SettingsIndex extends Component {
|
|||||||
{loading ?
|
{loading ?
|
||||||
<Loader />
|
<Loader />
|
||||||
:
|
:
|
||||||
<SettingsTabs data={data} />
|
<SettingsTabs data={data} refreshConfig={this.props.refreshConfig} />
|
||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
<Footer />
|
<Footer />
|
||||||
|
|||||||
@@ -11,13 +11,14 @@ import GraphsSettings from './tabs/GraphsSettings';
|
|||||||
import HealthchecksSettings from './tabs/HealthchecksSettings';
|
import HealthchecksSettings from './tabs/HealthchecksSettings';
|
||||||
import NotificationsSettings from './tabs/NotificationsSettings';
|
import NotificationsSettings from './tabs/NotificationsSettings';
|
||||||
import Authentication from '../Authentication/Authentication';
|
import Authentication from '../Authentication/Authentication';
|
||||||
|
import TableSettings from './tabs/TableSettings';
|
||||||
|
|
||||||
export default class SettingsTabs extends Component {
|
export default class SettingsTabs extends Component {
|
||||||
constructor(props) {
|
constructor(props) {
|
||||||
super(props)
|
super(props)
|
||||||
|
|
||||||
this.state = {
|
this.state = {
|
||||||
tab: "General",
|
tab: "Tables",
|
||||||
data: this.props.data
|
data: this.props.data
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -26,6 +27,7 @@ export default class SettingsTabs extends Component {
|
|||||||
var tabs = [
|
var tabs = [
|
||||||
'General',
|
'General',
|
||||||
'Graphs',
|
'Graphs',
|
||||||
|
'Tables',
|
||||||
'Notifications',
|
'Notifications',
|
||||||
'healthchecks.io',
|
'healthchecks.io',
|
||||||
'Reset',
|
'Reset',
|
||||||
@@ -121,6 +123,11 @@ export default class SettingsTabs extends Component {
|
|||||||
data={data.Graphs}
|
data={data.Graphs}
|
||||||
generateInputs={this.generateInputs}
|
generateInputs={this.generateInputs}
|
||||||
save={this.save} />
|
save={this.save} />
|
||||||
|
case 'Tables':
|
||||||
|
return <TableSettings
|
||||||
|
data={data.Tables}
|
||||||
|
refreshConfig={this.props.refreshConfig}
|
||||||
|
save={this.save} />
|
||||||
case 'Notifications':
|
case 'Notifications':
|
||||||
return <NotificationsSettings
|
return <NotificationsSettings
|
||||||
data={data.Notifications}
|
data={data.Notifications}
|
||||||
|
|||||||
123
resources/js/components/Settings/tabs/TableSettings.js
vendored
Normal file
123
resources/js/components/Settings/tabs/TableSettings.js
vendored
Normal file
@@ -0,0 +1,123 @@
|
|||||||
|
import React, { Component } from 'react';
|
||||||
|
import ReactDOM from 'react-dom';
|
||||||
|
import { Modal, Button, Tab } from 'react-bootstrap';
|
||||||
|
import Axios from 'axios';
|
||||||
|
import { toast } from 'react-toastify';
|
||||||
|
import { DragDropContext, Draggable, Droppable } from 'react-beautiful-dnd';
|
||||||
|
|
||||||
|
export default class TableSettings extends Component {
|
||||||
|
constructor(props) {
|
||||||
|
super(props)
|
||||||
|
|
||||||
|
this.state = {
|
||||||
|
visible: this.props.data[0],
|
||||||
|
hidden: this.props.data[1],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
handleOnDragEnd = (result) => {
|
||||||
|
if (!result.destination) return;
|
||||||
|
|
||||||
|
let visible = this.state.visible;
|
||||||
|
let hidden = this.state.hidden;
|
||||||
|
|
||||||
|
let from = result.source.droppableId == 'visibleColumns' ? visible.obj.value : hidden.obj.value;
|
||||||
|
let to = result.destination.droppableId == 'visibleColumns' ? visible.obj.value : hidden.obj.value;
|
||||||
|
|
||||||
|
let [reorderedItem] = from.splice(result.source.index, 1);
|
||||||
|
to.splice(result.destination.index, 0, reorderedItem);
|
||||||
|
|
||||||
|
this.setState({
|
||||||
|
visible: visible,
|
||||||
|
hidden: hidden
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
save = () => {
|
||||||
|
var url = 'api/settings/bulk?token=' + window.token;
|
||||||
|
|
||||||
|
Axios.post(url, {
|
||||||
|
data: [
|
||||||
|
{
|
||||||
|
name: 'visible_columns',
|
||||||
|
value: this.state.visible.obj.value
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'hidden_columns',
|
||||||
|
value: this.state.hidden.obj.value
|
||||||
|
}
|
||||||
|
],
|
||||||
|
})
|
||||||
|
.then((resp) => {
|
||||||
|
toast.success('Table settings updated');
|
||||||
|
this.props.refreshConfig();
|
||||||
|
})
|
||||||
|
.catch((err) => {
|
||||||
|
toast.error('Something went wrong');
|
||||||
|
console.log(err);
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
let visible = this.state.visible;
|
||||||
|
let hidden = this.state.hidden;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Tab.Content>
|
||||||
|
<div>
|
||||||
|
<p>{visible.obj.description}</p>
|
||||||
|
|
||||||
|
<DragDropContext onDragEnd={this.handleOnDragEnd}>
|
||||||
|
<div className="card pt-4 pb-2 px-4 mb-4">
|
||||||
|
<h4>Visible Columns</h4>
|
||||||
|
<Droppable droppableId="visibleColumns">
|
||||||
|
{(provided) => (
|
||||||
|
<ul className="visibleColumns pl-0" {...provided.droppableProps} ref={provided.innerRef}>
|
||||||
|
{visible.obj.value.map((e, i) => {
|
||||||
|
return (
|
||||||
|
<Draggable draggableId={e} index={i} key={e}>
|
||||||
|
{(provided) => (
|
||||||
|
<li className="card bg-secondary py-2 px-3 my-2" key={e} ref={provided.innerRef} {...provided.draggableProps} {...provided.dragHandleProps}>{e}</li>
|
||||||
|
)}
|
||||||
|
</Draggable>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
{provided.placeholder}
|
||||||
|
</ul>
|
||||||
|
)}
|
||||||
|
</Droppable>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="card pt-4 pb-2 px-4">
|
||||||
|
<h4>Hidden Columns</h4>
|
||||||
|
<Droppable droppableId="hiddenColumns pl-0">
|
||||||
|
{(provided) => (
|
||||||
|
<ul className="hiddenColumns pl-0" {...provided.droppableProps} ref={provided.innerRef}>
|
||||||
|
{hidden.obj.value.map((e, i) => {
|
||||||
|
return (
|
||||||
|
<Draggable draggableId={e} index={i} key={e}>
|
||||||
|
{(provided) => (
|
||||||
|
<li className="card bg-secondary py-2 px-3 my-2" key={e} ref={provided.innerRef} {...provided.draggableProps} {...provided.dragHandleProps}>{e}</li>
|
||||||
|
)}
|
||||||
|
</Draggable>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
{provided.placeholder}
|
||||||
|
</ul>
|
||||||
|
)}
|
||||||
|
</Droppable>
|
||||||
|
</div>
|
||||||
|
</DragDropContext>
|
||||||
|
|
||||||
|
<div className="mt-3">
|
||||||
|
<button className="btn btn-primary" onClick={() => { this.save() }}>Save</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</Tab.Content>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (document.getElementById('TableSettings')) {
|
||||||
|
ReactDOM.render(<TableSettings />, document.getElementById('TableSettings'));
|
||||||
|
}
|
||||||
2
resources/js/index.js
vendored
2
resources/js/index.js
vendored
@@ -94,7 +94,7 @@ export default class Index extends Component {
|
|||||||
)} />
|
)} />
|
||||||
<Route exact path={window.config.base + 'settings'} render={(props) => (
|
<Route exact path={window.config.base + 'settings'} render={(props) => (
|
||||||
<div>
|
<div>
|
||||||
<SettingsIndex />
|
<SettingsIndex refreshConfig={this.getConfig} />
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
)} />
|
)} />
|
||||||
|
|||||||
Reference in New Issue
Block a user