Adding new tables to paginate results with many rows
This commit is contained in:
parent
01de175fe9
commit
a14398f906
11 changed files with 495 additions and 78 deletions
|
@ -29,4 +29,13 @@ $routes->group('register', static function ($routes) {
|
|||
$routes->post('newuser', 'Register::newuser');
|
||||
$routes->get('new_captcha', 'Register::newCaptcha');
|
||||
$routes->get('ok', 'Register::ok');
|
||||
});
|
||||
|
||||
$routes->group('api', static function ($routes) {
|
||||
$routes->get('bests_laps', 'Api::getBestsLaps');
|
||||
$routes->get('most_active_users', 'Api::getMostActiveUsers');
|
||||
});
|
||||
|
||||
$routes->group('test', static function ($routes) {
|
||||
$routes->get('tables', 'Test::tables');
|
||||
});
|
63
app/Controllers/Api.php
Normal file
63
app/Controllers/Api.php
Normal file
|
@ -0,0 +1,63 @@
|
|||
<?php
|
||||
|
||||
namespace App\Controllers;
|
||||
|
||||
use App\Controllers\BaseController;
|
||||
use App\Models\BestLapsModel;
|
||||
use App\Models\CarCatsModel;
|
||||
use CodeIgniter\API\ResponseTrait;
|
||||
|
||||
class Api extends BaseController
|
||||
{
|
||||
use ResponseTrait;
|
||||
|
||||
public function getBestsLaps()
|
||||
{
|
||||
$bestLapsModel = new BestLapsModel;
|
||||
$period = $this->request->getGet('period');
|
||||
$carCatId = $this->request->getGet('car_cat');
|
||||
|
||||
if (!$period || !$carCatId) return $this->fail('The period and/or category were not indicated');
|
||||
|
||||
$page = $this->request->getGet('page');
|
||||
$limit = $this->request->getGet('limit');
|
||||
if (!$page | !is_numeric($page)) $page = 0;
|
||||
if (!$limit | !is_numeric($limit)) $limit = 0;
|
||||
|
||||
[$list, $total] = $bestLapsModel->getBests($period, $carCatId, $page, $limit);
|
||||
|
||||
return $this->respond(['data' => $list, 'total' => $total]);
|
||||
}
|
||||
|
||||
public function getMostActiveUsers()
|
||||
{
|
||||
$period = $this->request->getGet('period');
|
||||
$carCatId = $this->request->getGet('car_cat');
|
||||
|
||||
if (!$period || !$carCatId) return $this->fail('The period and/or category were not indicated');
|
||||
|
||||
$backto = getDateDiff($period);
|
||||
$carCatsModel = new CarCatsModel;
|
||||
|
||||
$carsCatIds = $carCatsModel->getCarsInCat($carCatId);
|
||||
|
||||
$builder = $this->db->table('races r');
|
||||
$builder->select('COUNT(*) AS count, u.username');
|
||||
$builder->join('users u', 'u.id = r.user_id');
|
||||
$builder->where('UNIX_TIMESTAMP(r.timestamp) >', $backto);
|
||||
$builder->whereIn('r.car_id', $carsCatIds);
|
||||
$builder->groupBy('u.username');
|
||||
$builder->orderBy('count DESC');
|
||||
|
||||
$query = $builder->get(20);
|
||||
$list = [];
|
||||
$total = 0;
|
||||
|
||||
if ($query && $query->getNumRows() > 0) {
|
||||
$list = $query->getResult();
|
||||
$total = $query->getNumRows();
|
||||
}
|
||||
|
||||
return $this->respond(['data' => $list, 'total' => $total]);
|
||||
}
|
||||
}
|
|
@ -84,13 +84,14 @@ class Home extends BaseController
|
|||
$categoriesList = $carCatModel->select('id, name, count(carId) as totalCars')->groupBy('id')->findAll();
|
||||
$currCat = $carCatModel->find($carCatId);
|
||||
|
||||
$carsCatList = $carCatModel->select('carId')->where('id', $carCatId)->findAll();
|
||||
//$carsCatList = $carCatModel->select('carId')->where('id', $carCatId)->findAll();
|
||||
$carsCatIds = $carCatModel->getCarsInCat($carCatId);
|
||||
|
||||
$tplData['currCat'] = $currCat;
|
||||
$tplData['carCategoriesList'] = $categoriesList;
|
||||
|
||||
$carsCatIds = [];
|
||||
foreach ($carsCatList as $car) $carsCatIds[] = $car->carId;
|
||||
//$carsCatIds = [];
|
||||
//foreach ($carsCatList as $car) $carsCatIds[] = $car->carId;
|
||||
|
||||
//UGLY: there is some category that have no car assigned so create a fake $carsql for them
|
||||
//to prevent errors in the generated queries
|
||||
|
@ -108,6 +109,7 @@ class Home extends BaseController
|
|||
################################
|
||||
*/
|
||||
|
||||
/*
|
||||
$builder = $this->db->table('races r');
|
||||
$builder->select('r.user_id, COUNT(*) AS count, u.username');
|
||||
$builder->join('users u', 'u.id = r.user_id');
|
||||
|
@ -119,6 +121,7 @@ class Home extends BaseController
|
|||
$tplData['users'] = [];
|
||||
$query = $builder->get();
|
||||
if ($query && $query->getNumRows() > 0) $tplData['users'] = $query->getResult();
|
||||
*/
|
||||
|
||||
/*
|
||||
################################
|
||||
|
@ -126,8 +129,7 @@ class Home extends BaseController
|
|||
## WITH A CAR OFT HIS CATEGORY
|
||||
################################
|
||||
*/
|
||||
|
||||
$tplData['mylaps'] = $bestLapsModel->getBests($period, $carCatId, 0, 0);
|
||||
//list($tplData['mylaps'], $_) = $bestLapsModel->getBests($period, $carCatId, 0, 0);
|
||||
|
||||
$tplData['tracks'] = [];
|
||||
$builder = $this->db->table('races');
|
||||
|
@ -173,9 +175,9 @@ class Home extends BaseController
|
|||
$query = $builder->get();
|
||||
if ($query && $query->getNumRows() > 0) $tplData['cars'] = $query->getResult();
|
||||
|
||||
echo get_header('Home');
|
||||
echo get_header('Home', ['minidt.css']);
|
||||
echo view('main', $tplData);
|
||||
echo get_footer();
|
||||
echo get_footer(['minidt.js', 'home_tables.js']);
|
||||
}
|
||||
|
||||
public function error404()
|
||||
|
|
|
@ -9,7 +9,7 @@ class BestLapsModel extends BaseModel
|
|||
|
||||
public function getBests(string $period, string $carCat, int $page=0, int $limit=20)
|
||||
{
|
||||
$from = $page * $limit;
|
||||
$offset = $page * $limit;
|
||||
$list = [];
|
||||
|
||||
switch ($period)
|
||||
|
@ -36,7 +36,19 @@ class BestLapsModel extends BaseModel
|
|||
break;
|
||||
}
|
||||
|
||||
$total = 0;
|
||||
$builder = $this->builder();
|
||||
$builder->select('COUNT(*) AS total');
|
||||
$builder->join('laps l', 'l.id = bl.lap_id');
|
||||
$builder->join('races r', 'r.id = bl.race_id');
|
||||
$builder->where('UNIX_TIMESTAMP(r.timestamp) >', $backto);
|
||||
$builder->where('bl.car_cat', $carCat);
|
||||
$builder->groupBy(['r.track_id', 'l.wettness']);
|
||||
$query = $builder->get();
|
||||
|
||||
if ($query) $total = $query->getNumRows();
|
||||
|
||||
$builder->resetQuery();
|
||||
$builder->join('laps l', 'l.id = bl.lap_id');
|
||||
$builder->select('l.race_id, r.track_id, r.car_id, r.user_id, r.timestamp, l.wettness, bl.laptime AS bestlap, c.name AS car_name, t.name AS track_name, u.username');
|
||||
$builder->join('races r', 'r.id = bl.race_id');
|
||||
|
@ -46,11 +58,11 @@ class BestLapsModel extends BaseModel
|
|||
$builder->where('UNIX_TIMESTAMP(r.timestamp) >', $backto);
|
||||
$builder->where('bl.car_cat', $carCat);
|
||||
$builder->groupBy(['r.track_id', 'l.wettness']);
|
||||
if ($limit > 0) $builder->limit($from, $limit);
|
||||
if ($limit > 0) $builder->limit($limit, $offset);
|
||||
$query = $builder->get();
|
||||
|
||||
if ($query && $query->getNumRows() > 0) $list = $query->getResult();
|
||||
|
||||
return $list;
|
||||
return [$list, $total];
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,4 +6,19 @@ class CarCatsModel extends BaseModel
|
|||
{
|
||||
protected $table = 'cars_cats';
|
||||
protected $allowedFields = ['id', 'name', 'carId'];
|
||||
|
||||
/**
|
||||
* Return all cars in the indicated category
|
||||
* @param mixed $carCatId The ID od the category
|
||||
* @return array A array with all cars ID in teh category
|
||||
*/
|
||||
public function getCarsInCat($carCatId)
|
||||
{
|
||||
$carsCatList = $this->select('carId')->where('id', $carCatId)->findAll();
|
||||
|
||||
$carsCatIds = [];
|
||||
foreach ($carsCatList as $car) $carsCatIds[] = $car->carId;
|
||||
|
||||
return $carsCatIds;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -57,77 +57,17 @@
|
|||
Most active users<br />
|
||||
<small><?= $periodString; ?></small>
|
||||
</h3>
|
||||
<table class="fullPage responsive cat-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Pilot</th>
|
||||
<th>Races</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php
|
||||
foreach ($users as $user):
|
||||
?>
|
||||
<tr>
|
||||
<td data-title="Pilot">
|
||||
<?= clickableName($user->username, 'user', $user->username) ?>
|
||||
</td>
|
||||
<td data-title="Races">
|
||||
<?= $user->count ?>
|
||||
</td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
</tbody>
|
||||
</table>
|
||||
<div class="table-container">
|
||||
<table id="most_active_users" class="responsive cat-table"></table>
|
||||
</div>
|
||||
|
||||
<h3>
|
||||
Bests lap for each track<br />
|
||||
<small><?=$periodString; ?></small>
|
||||
</h3>
|
||||
<table class="fullPage responsive cat-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Track</th>
|
||||
<th>Pilot</th>
|
||||
<th>Car</th>
|
||||
<th>Laptime</th>
|
||||
<th>Weather</th>
|
||||
<th>Date</th>
|
||||
<th>Session</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php
|
||||
foreach ($mylaps as $mylap):
|
||||
$track = $mylap->track_id;
|
||||
$car = $mylap->car_id;
|
||||
?>
|
||||
<tr>
|
||||
<td data-title="Track">
|
||||
<?= clickableName($mylap->track_id, 'track', $mylap->track_name) ?>
|
||||
</td>
|
||||
<td data-title="Pilot">
|
||||
<?= clickableName($mylap->username, 'user', $mylap->username) ?>
|
||||
</td>
|
||||
<td data-title="Car">
|
||||
<?= clickableName($mylap->car_id, 'car', $mylap->car_name) ?>
|
||||
</td>
|
||||
<td data-title="Laptime">
|
||||
<?= formatLaptime($mylap->bestlap); ?>
|
||||
</td>
|
||||
<td data-title="Weather">
|
||||
<?= weatherTag($mylap->wettness); ?>
|
||||
</td>
|
||||
<td data-title="Date">
|
||||
<?= $mylap->timestamp; ?>
|
||||
</td>
|
||||
<td data-title="Session">
|
||||
<a href="<?= base_url() ?>race/<?= $mylap->race_id ?>">#<?=$mylap->race_id?></a>
|
||||
</td>
|
||||
</tr>
|
||||
<?php endforeach ?>
|
||||
</tbody>
|
||||
</table>
|
||||
</h3>
|
||||
<div class="table-container">
|
||||
<table id="best_laps" class="responsive"></table>
|
||||
</div>
|
||||
|
||||
<h3>
|
||||
Most used Tracks<br />
|
||||
|
|
|
@ -8,6 +8,11 @@
|
|||
<title><?=$title?></title>
|
||||
<link rel="stylesheet" type="text/css" href="<?= base_url() ?>/css/style.css" />
|
||||
<link rel="stylesheet" type="text/css" href="<?= base_url() ?>/font/weather-icons.css" />
|
||||
<?php if(!empty($custom_css)): ?>
|
||||
<?php foreach ($custom_css as $css): ?>
|
||||
<link rel="stylesheet" type="text/css" href="<?=base_url()."/css/$css"?>"></script>
|
||||
<?php endforeach ?>
|
||||
<?php endif ?>
|
||||
</head>
|
||||
<body>
|
||||
<nav class="mainMenu">
|
||||
|
|
89
public/css/minidt.css
Normal file
89
public/css/minidt.css
Normal file
|
@ -0,0 +1,89 @@
|
|||
.table-container {
|
||||
margin: 1rem auto;
|
||||
max-height: 70vh;
|
||||
position: relative;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.mini-dt thead th,
|
||||
.mini-dt tfoot th {
|
||||
position: sticky;
|
||||
background-color: #fff;
|
||||
}
|
||||
|
||||
.mini-dt thead th {
|
||||
top: 0;
|
||||
}
|
||||
|
||||
.mini-dt th,
|
||||
.mini-dt td {
|
||||
padding: .5rem;
|
||||
}
|
||||
|
||||
.mini-dt tfoot th {
|
||||
bottom: 0;
|
||||
}
|
||||
|
||||
.mini-dt thead,
|
||||
.mini-dt tbody,
|
||||
.mini-dt tfoot {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.mini-dt tfoot.hide {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.mini-dt th.text-center,
|
||||
.mini-dt td.text-center {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.mini-dt th.text-right,
|
||||
.mini-dt td.text-right {
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.pagination {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: end;
|
||||
width: 100%;
|
||||
gap: .5rem;
|
||||
color: #666;
|
||||
padding: 1rem;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.pagination button {
|
||||
background: transparent;
|
||||
border: 1px solid #666;
|
||||
border-radius: 3px;
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
}
|
||||
|
||||
.pagination button::after {
|
||||
display: block;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
content: "";
|
||||
mask-repeat: no-repeat;
|
||||
mask-position: center;
|
||||
background-color: #666;
|
||||
mask-size: 100%;
|
||||
}
|
||||
|
||||
.pagination .prev-btn::after {
|
||||
mask-image: url("data:image/svg+xml;base64,PHN2ZyB3aWR0aD0nMjQnIGhlaWdodD0nMjQnIHZpZXdCb3g9JzAgMCAyNCAyNCcgZmlsbD0nbm9uZScgeG1sbnM9J2h0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnJwo+PHBhdGggZD0nTTE2LjI0MjYgNi4zNDMxN0wxNC44Mjg0IDQuOTI4OTZMNy43NTczOSAxMkwxNC44Mjg1IDE5LjA3MTFMMTYuMjQyNyAxNy42NTY5TDEwLjU4NTggMTJMMTYuMjQyNiA2LjM0MzE3WicgZmlsbD0nY3VycmVudENvbG9yJyAvPjwvc3ZnPg==");
|
||||
}
|
||||
|
||||
.pagination .next-btn::after {
|
||||
mask-image: url("data:image/svg+xml;base64,PHN2ZyB3aWR0aD0nMjQnIGhlaWdodD0nMjQnIHZpZXdCb3g9JzAgMCAyNCAyNCcgZmlsbD0nbm9uZScgeG1sbnM9J2h0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnJwo+PHBhdGggZD0nTTEwLjU4NTggNi4zNDMxN0wxMiA0LjkyODk2TDE5LjA3MTEgMTJMMTIgMTkuMDcxMUwxMC41ODU4IDE3LjY1NjlMMTYuMjQyNyAxMkwxMC41ODU4IDYuMzQzMTdaJyBmaWxsPSdjdXJyZW50Q29sb3InIC8+PC9zdmc+");
|
||||
}
|
||||
|
||||
.page-selector {
|
||||
border: 0;
|
||||
color: #666;
|
||||
margin: 0;
|
||||
}
|
|
@ -2,4 +2,37 @@ $('#menu-select').on('change', function() {
|
|||
location.href = this.value;
|
||||
});
|
||||
|
||||
if (typeof period !== 'undefined') $(`a#${period}`).addClass('selected')
|
||||
if (typeof period !== 'undefined') $(`a#${period}`).addClass('selected')
|
||||
|
||||
function formatLaptime(time) {
|
||||
const date = new Date(time * 1000);
|
||||
const minutes = String(date.getMinutes()).padStart(2, '0')
|
||||
const seconds = String(date.getSeconds()).padStart(2, '0')
|
||||
const miliseconds = date.getMilliseconds()
|
||||
|
||||
return `${minutes}:${seconds}:${miliseconds}`
|
||||
}
|
||||
|
||||
function weatherTag(weather)
|
||||
{
|
||||
switch(weather)
|
||||
{
|
||||
case 0:
|
||||
return '<i class="wi wi-day-sunny"></i>';
|
||||
case 1:
|
||||
return '<i class="wi wi-rain"></i>';
|
||||
case 2:
|
||||
return '<i class="wi wi-rain"></i>';
|
||||
case 3:
|
||||
return '<i class="wi wi-rain"></i>';
|
||||
default:
|
||||
return ''
|
||||
}
|
||||
}
|
||||
|
||||
function linkTag(id, type , content) {
|
||||
const url = `${base_url}/${type}/${id}`
|
||||
if (type == 'race') content = `#${content}`
|
||||
|
||||
return `<a href="${url}">${content}</a>`;
|
||||
}
|
83
public/js/home_tables.js
Normal file
83
public/js/home_tables.js
Normal file
|
@ -0,0 +1,83 @@
|
|||
const urlParams = new URL(window.location).searchParams
|
||||
const car_cat = (!!urlParams.get('cat')) ? urlParams.get('cat') : 'TRB1'
|
||||
const period = (!!urlParams.get('period')) ? urlParams.get('period') : 'today'
|
||||
|
||||
const dt_active_users = new MiniDT({
|
||||
target: 'most_active_users',
|
||||
url: `${base_url}/api/most_active_users`,
|
||||
cols: [
|
||||
{
|
||||
title: 'Pilot',
|
||||
col: 'username',
|
||||
render: (row) => {
|
||||
return linkTag(row.username, 'user', row.username)
|
||||
}
|
||||
},
|
||||
{
|
||||
title: 'Races',
|
||||
col: 'count',
|
||||
},
|
||||
],
|
||||
params: {
|
||||
period: period,
|
||||
car_cat: car_cat
|
||||
}
|
||||
})
|
||||
|
||||
const dt_bests_laps = new MiniDT({
|
||||
target: 'best_laps',
|
||||
url: `${base_url}/api/bests_laps`,
|
||||
cols: [
|
||||
{
|
||||
title: 'Track',
|
||||
col: 'track_name',
|
||||
render: (row) => {
|
||||
return linkTag(row.track_id, 'track', row.track_name)
|
||||
}
|
||||
},
|
||||
{
|
||||
title: 'Pilot',
|
||||
col: 'username',
|
||||
render: (row) => {
|
||||
return linkTag(row.username, 'user', row.username)
|
||||
}
|
||||
},
|
||||
{
|
||||
title: 'Car',
|
||||
col: 'car_name',
|
||||
render: (row) => {
|
||||
return linkTag(row.car_id, 'car', row.car_name)
|
||||
}
|
||||
},
|
||||
{
|
||||
title: 'Laptime',
|
||||
col: 'bestlap',
|
||||
render: (row) => {
|
||||
return formatLaptime(row.bestlap)
|
||||
}
|
||||
},
|
||||
{
|
||||
title: 'Weather',
|
||||
col: 'wettness',
|
||||
align: 'center',
|
||||
render: (row) => {
|
||||
return weatherTag(parseInt(row.wettness))
|
||||
}
|
||||
},
|
||||
{
|
||||
title: 'Date',
|
||||
col: 'timestamp'
|
||||
},
|
||||
{
|
||||
title: 'Session',
|
||||
col: 'race_id',
|
||||
render: (row) => {
|
||||
return linkTag(row.race_id, 'race', row.race_id)
|
||||
}
|
||||
},
|
||||
],
|
||||
params: {
|
||||
period: period,
|
||||
car_cat: car_cat
|
||||
}
|
||||
})
|
166
public/js/minidt.js
Normal file
166
public/js/minidt.js
Normal file
|
@ -0,0 +1,166 @@
|
|||
class MiniDT {
|
||||
constructor(config) {
|
||||
if (!config.url || !config.target || !config.cols) return false;
|
||||
|
||||
this.rows = []
|
||||
this.page = 0
|
||||
this.total = 0
|
||||
this.total_pages = 0
|
||||
this.limit = (!!config.limit) ? config.limit : 20
|
||||
this.params = (!!config.params) ? config.params : {}
|
||||
this.url = config.url
|
||||
this.cols = {}
|
||||
this.target = document.getElementById(config.target)
|
||||
if (!this.target.classList.contains('mini-dt')) this.target.classList.add('mini-dt')
|
||||
|
||||
// Create table header, body and footer
|
||||
this.thead = this.target.createTHead()
|
||||
this.tbody = this.target.createTBody()
|
||||
this.tfoot = this.target.createTFoot()
|
||||
this.tfoot.classList.add('hide')
|
||||
|
||||
// Draw header columns
|
||||
const head_row = this.thead.insertRow()
|
||||
|
||||
config.cols.forEach( col => {
|
||||
const th = document.createElement('th')
|
||||
th.innerText = col.title
|
||||
|
||||
if (!!col.align && ['center', 'right'].includes(col.align)) th.classList.add(`text-${col.align}`)
|
||||
|
||||
// Las columnas las almacenamos de este modo para ahorrar tener que recorrer el array cada vez
|
||||
this.cols[col.col] = {
|
||||
title: col.title,
|
||||
render: (!!col.render && typeof col.render === 'function') ? col.render : null,
|
||||
align: (!!col.align && ['center', 'right'].includes(col.align)) ? col.align: null
|
||||
}
|
||||
|
||||
// Añadimos la columna a la cabecera
|
||||
head_row.appendChild(th)
|
||||
});
|
||||
|
||||
// Y añadimos la fila a la cabecera
|
||||
this.thead.appendChild(head_row)
|
||||
|
||||
// Draw footer
|
||||
const row_footer = this.tfoot.insertRow()
|
||||
const th = document.createElement('th')
|
||||
th.setAttribute('colspan', Object.keys(this.cols).length)
|
||||
|
||||
// Pagination container
|
||||
this.pagination_container = document.createElement('div')
|
||||
this.pagination_container.classList.add('pagination')
|
||||
|
||||
// Pagination text
|
||||
this.pageText = document.createTextNode(`Page 1 of 1`)
|
||||
this.pagination_container.appendChild(this.pageText)
|
||||
|
||||
// Previous page button
|
||||
this.prev_btn = document.createElement('button')
|
||||
//this.prev_btn.innerText = '<'
|
||||
this.prev_btn.classList.add('prev-btn')
|
||||
this.prev_btn.addEventListener('click', this.previous)
|
||||
this.pagination_container.appendChild(this.prev_btn)
|
||||
|
||||
// Page selector
|
||||
this.pageSelector = document.createElement('select')
|
||||
this.pageSelector.classList.add('page-selector')
|
||||
this.pageSelector.addEventListener('change', this.changePage)
|
||||
this.pagination_container.appendChild(this.pageSelector)
|
||||
|
||||
// Next page button
|
||||
this.next_btn = document.createElement('button')
|
||||
//this.next_btn.innerText = '>'
|
||||
this.next_btn.classList.add('next-btn')
|
||||
this.next_btn.addEventListener('click', this.next)
|
||||
this.pagination_container.appendChild(this.next_btn)
|
||||
|
||||
th.appendChild(this.pagination_container)
|
||||
row_footer.appendChild(th);
|
||||
|
||||
this.getData()
|
||||
}
|
||||
|
||||
getData = async () => {
|
||||
this.rows = []
|
||||
this.params.limit = this.limit
|
||||
this.params.page = this.page
|
||||
|
||||
try {
|
||||
const resp = await fetch(this.url + '?' + new URLSearchParams(this.params).toString())
|
||||
const data = await resp.json()
|
||||
if (!!data.data) this.rows = data.data
|
||||
this.total = (!!data.total) ? data.total : 0
|
||||
|
||||
if (this.total > 0 && this.total > this.limit) this.total_pages = Math.ceil(this.total / this.limit)
|
||||
else this.total_pages = 1
|
||||
|
||||
this.pageText.textContent = `Page ${this.page + 1} of ${this.total_pages}`
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
}
|
||||
|
||||
this.render()
|
||||
}
|
||||
|
||||
/**
|
||||
* Rendering the component
|
||||
*/
|
||||
render = () => {
|
||||
this.tbody.innerHTML = ''
|
||||
|
||||
if (this.rows.length == 0) {
|
||||
const row = this.tbody.insertRow()
|
||||
const td = document.createElement('td')
|
||||
td.setAttribute('colspan', Object.keys(this.cols).length)
|
||||
td.innerHTML = '<strong>No data</strong>'
|
||||
row.appendChild(td)
|
||||
this.tfoot.classList.add('hide')
|
||||
} else {
|
||||
this.rows.forEach( row => {
|
||||
const tr = this.tbody.insertRow()
|
||||
|
||||
Object.keys(this.cols).forEach( key => {
|
||||
if (key in row) {
|
||||
const cell = tr.insertCell()
|
||||
cell.setAttribute('data-title', this.cols[key].title)
|
||||
if (!!this.cols[key].align) cell.classList.add(`text-${this.cols[key].align}`)
|
||||
|
||||
cell.innerHTML = (!!this.cols[key].render)
|
||||
? this.cols[key].render(row)
|
||||
: cell.innerText = row[key]
|
||||
}
|
||||
})
|
||||
})
|
||||
this.tfoot.classList.remove('hide')
|
||||
}
|
||||
|
||||
this.pageSelector.removeEventListener('change', this.changePage)
|
||||
this.pageSelector.innerHTML = ''
|
||||
|
||||
for (let i=0; i < this.total_pages; i++) {
|
||||
this.pageSelector.append(new Option(i + 1, i, i == this.page))
|
||||
}
|
||||
|
||||
this.pageSelector.value = this.page
|
||||
this.pageSelector.addEventListener('change', this.changePage)
|
||||
}
|
||||
|
||||
changePage = (value) => {
|
||||
this.page = (value instanceof Event) ? parseInt(value.target.value) : parseInt(value)
|
||||
this.pageText.textContent = `Page ${this.page + 1} of ${this.total_pages}`
|
||||
this.getData()
|
||||
}
|
||||
|
||||
previous = () => {
|
||||
if (this.page > 0) this.page--
|
||||
this.pageText.textContent = `Page ${this.page + 1} of ${this.total_pages}`
|
||||
this.getData()
|
||||
}
|
||||
|
||||
next = () => {
|
||||
if (this.page < this.total_pages - 1) this.page++
|
||||
this.pageText.textContent = `Page ${this.page + 1} of ${this.total_pages}`
|
||||
this.getData()
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue