feat: add pagination

This commit is contained in:
Martin Eyben 2024-12-04 10:50:01 +00:00
parent c0c050a49c
commit a696c3a5c9
12 changed files with 571 additions and 222 deletions

View File

@ -0,0 +1,283 @@
/*****************************************************
* Paginator Function *
*****************************************************
* config : {
* get_rows : function used to select rows to do pagination on
* If no function is provided, checks for a config.table element and looks for rows in there to page
*
* box : Empty element that will have page buttons added to it
* If no config.box is provided, but a config.table is, then the page buttons will be added using the table
*
* table : table element to be paginated
* not required if a get_rows function is provided
*
* rows_per_page : number of rows to display per page
* default number is 10
*
* page: page to display
* default page is 1
*
* box_mode: "list", "buttons", or function. determines how the page number buttons are built.
* "list" builds the page index in list format and adds class "pagination" to the ul element. Meant for use with bootstrap
* "buttons" builds the page index out of buttons
* if this field is a function, it will be passed the config object as its only param and assumed to build the page index buttons
*
* page_options: false or [{text: , value: }, ... ] used to set what the dropdown menu options are available, resets rows_per_page value
* false prevents the options from being displayed
* [{text: , value: }, ... ] allows you to customize what values can be chosen, a value of 0 will display all the table's rows.
* the default setup is
* [
* { value: 5, text: '5' },
* { value: 10, text: '10' },
* { value: 20, text: '20' },
* { value: 50, text: '50' },
* { value: 100,text: '100' },
* { value: 0, text: 'All' }
* ]
*
* active_class: set the class for page buttons to have when active.
* defaults to "active"
*
* disable: true or false, shows all rows of the table and hides pagination controlls if set to true.
*
* tail_call: function to be called after paginator is done.
*
* }
*/
function paginator(config) {
// throw errors if insufficient parameters were given
if (typeof config != "object")
throw "Paginator was expecting a config object!";
if (typeof config.get_rows != "function" && !(config.table instanceof Element))
throw "Paginator was expecting a table or get_row function!";
// get/set if things are disabled
if (typeof config.disable == "undefined") {
config.disable = false;
}
// get/make an element for storing the page numbers in
var box;
if (!(config.box instanceof Element)) {
config.box = document.createElement("div");
}
box = config.box;
// get/make function for getting table's rows
if (typeof config.get_rows != "function") {
config.get_rows = function () {
var table = config.table
var tbody = table.getElementsByTagName("tbody")[0]||table;
// get all the possible rows for paging
// exclude any rows that are just headers or empty
children = tbody.children;
var trs = [];
for (var i=0;i<children.length;i++) {
if (children[i].nodeType = "tr") {
if (children[i].getElementsByTagName("td").length > 0) {
trs.push(children[i]);
}
}
}
return trs;
}
}
var get_rows = config.get_rows;
var trs = get_rows();
// get/set rows per page
if (typeof config.rows_per_page == "undefined") {
var selects = box.getElementsByTagName("select");
if (typeof selects != "undefined" && (selects.length > 0 && typeof selects[0].selectedIndex != "undefined")) {
config.rows_per_page = selects[0].options[selects[0].selectedIndex].value;
} else {
config.rows_per_page = 10;
}
}
var rows_per_page = config.rows_per_page;
// get/set current page
if (typeof config.page == "undefined") {
config.page = 1;
}
var page = config.page;
// get page count
var pages = (rows_per_page > 0)? Math.ceil(trs.length / rows_per_page):1;
// check that page and page count are sensible values
if (pages < 1) {
pages = 1;
}
if (page > pages) {
page = pages;
}
if (page < 1) {
page = 1;
}
config.page = page;
// hide rows not on current page and show the rows that are
for (var i=0;i<trs.length;i++) {
if (typeof trs[i]["data-display"] == "undefined") {
trs[i]["data-display"] = trs[i].style.display||"";
}
if (rows_per_page > 0) {
if (i < page*rows_per_page && i >= (page-1)*rows_per_page) {
trs[i].style.display = trs[i]["data-display"];
} else {
// Only hide if pagination is not disabled
if (!config.disable) {
trs[i].style.display = "none";
} else {
trs[i].style.display = trs[i]["data-display"];
}
}
} else {
trs[i].style.display = trs[i]["data-display"];
}
}
// page button maker functions
config.active_class = config.active_class||"active";
if (typeof config.box_mode != "function" && config.box_mode != "list" && config.box_mode != "buttons") {
config.box_mode = "button";
}
if (typeof config.box_mode == "function") {
config.box_mode(config);
} else {
var make_button;
if (config.box_mode == "list") {
make_button = function (symbol, index, config, disabled, active) {
var li = document.createElement("li");
var a = document.createElement("a");
a.href = "#";
a.innerHTML = symbol;
a.addEventListener("click", function (event) {
event.preventDefault();
this.parentNode.click();
return false;
}, false);
li.appendChild(a);
var classes = [];
if (disabled) {
classes.push("disabled");
}
if (active) {
classes.push(config.active_class);
}
li.className = classes.join(" ");
li.addEventListener("click", function () {
if (this.className.split(" ").indexOf("disabled") == -1) {
config.page = index;
paginator(config);
}
}, false);
return li;
}
} else {
make_button = function (symbol, index, config, disabled, active) {
var button = document.createElement("button");
button.innerHTML = symbol;
button.addEventListener("click", function (event) {
event.preventDefault();
if (this.disabled != true) {
config.page = index;
paginator(config);
}
return false;
}, false);
if (disabled) {
button.disabled = true;
}
if (active) {
button.className = config.active_class;
}
return button;
}
}
// make page button collection
var page_box = document.createElement(config.box_mode == "list"?"ul":"div");
if (config.box_mode == "list") {
page_box.className = "pagination";
}
var left = make_button("&laquo;", (page>1?page-1:1), config, (page == 1), false);
page_box.appendChild(left);
for (var i=1;i<=pages;i++) {
var li = make_button(i, i, config, false, (page == i));
page_box.appendChild(li);
}
var right = make_button("&raquo;", (pages>page?page+1:page), config, (page == pages), false);
page_box.appendChild(right);
if (box.childNodes.length) {
while (box.childNodes.length > 1) {
box.removeChild(box.childNodes[0]);
}
box.replaceChild(page_box, box.childNodes[0]);
} else {
box.appendChild(page_box);
}
}
// make rows per page selector
if (!(typeof config.page_options == "boolean" && !config.page_options)) {
if (typeof config.page_options == "undefined") {
config.page_options = [
{ value: 5, text: '5' },
{ value: 10, text: '10' },
{ value: 20, text: '20' },
{ value: 50, text: '50' },
{ value: 100,text: '100' },
{ value: 0, text: 'All' }
];
}
var options = config.page_options;
var select = document.createElement("select");
for (var i=0;i<options.length;i++) {
var o = document.createElement("option");
o.value = options[i].value;
o.text = options[i].text;
select.appendChild(o);
}
select.value = rows_per_page;
select.addEventListener("change", function () {
config.rows_per_page = this.value;
paginator(config);
}, false);
box.appendChild(select);
}
// status message
var stat = document.createElement("span");
stat.innerHTML = "On page " + page + " of " + pages
+ ", showing rows " + (((page-1)*rows_per_page)+1)
+ " to " + (trs.length<page*rows_per_page||rows_per_page==0?trs.length:page*rows_per_page)
+ " of " + trs.length;
box.appendChild(stat);
// hide pagination if disabled
if (config.disable) {
if (typeof box["data-display"] == "undefined") {
box["data-display"] = box.style.display||"";
}
box.style.display = "none";
} else {
if (box.style.display == "none") {
box.style.display = box["data-display"]||"";
}
}
// run tail function
if (typeof config.tail_call == "function") {
config.tail_call(config);
}
return box;
}

View File

@ -0,0 +1,12 @@
window.addEventListener("load", function() {
const tablesWithBox = document.querySelectorAll(".table")
tablesWithBox.forEach(t => {
paginator({
table: t.children[0],
box: t.children[1],
active_class: "color_page"
});
});
}, false);

View File

@ -27,6 +27,8 @@
id : <%= box.id %>
date : <%= box.date %>
<h2>Liste des pièces</h2>
<div class="table">
<table>
<thead>
<tr>
@ -45,25 +47,32 @@
<% }); %>
</tbody>
</table>
<div class="box"></div>
</div>
<h2>Liste des modèles constructibles</h2>
<table>
<thead>
<tr>
<th>Id modèle</th>
<th>Nom</th>
<th></th>
</tr>
</thead>
<tbody>
<% box.models.forEach(function(model) { %>
<tr>
<td><%=model.id%></td>
<td><%=model.name%></td>
<td><a href="/models/byname/<%=model.name%>"><i class="fa-solid fa-eye"></i></a></td>
</tr>
<% }); %>
</tbody>
</table>
<div class="table">
<table>
<thead>
<tr>
<th>Id modèle</th>
<th>Nom</th>
<th></th>
</tr>
</thead>
<tbody>
<% box.models.forEach(function(model) { %>
<tr>
<td><%=model.id%></td>
<td><%=model.name%></td>
<td><a href="/models/byname/<%=model.name%>"><i class="fa-solid fa-eye"></i></a></td>
</tr>
<% }); %>
</tbody>
</table>
<div class="box"></div>
</div>
<h2>Ajouter une pièce</h2>
<form>

View File

@ -32,33 +32,38 @@
<hr>
<table>
<thead>
<tr>
<th>Id</th>
<th>Nom</th>
<th>Nombre de pièces</th>
<th>Date création</th>
<th></th>
</tr>
</thead>
<tbody>
<% boxes.forEach(function(box) { %>
<tr>
<td><%=box.id%></td>
<td><%=box.title%></td>
<td><%- box.pieces.reduce(
(p,q) => p + q.second, 0
)
-%></td>
<td><%=box.date.toLocaleDateString()%></td>
<td><a href="/boxes/byid/<%=box.id%>"><i class="fa-solid fa-eye"></i></a></td>
</tr>
<div class="table">
<% }); %>
<table>
<thead>
<tr>
<th>Id</th>
<th>Nom</th>
<th>Nombre de pièces</th>
<th>Date création</th>
<th></th>
</tr>
</thead>
<tbody>
<% boxes.forEach(function(box) { %>
<tr>
<td><%=box.id%></td>
<td><%=box.title%></td>
<td><%- box.pieces.reduce(
(p,q) => p + q.second, 0
)
-%></td>
<td><%=box.date.toLocaleDateString()%></td>
<td><a href="/boxes/byid/<%=box.id%>"><i class="fa-solid fa-eye"></i></a></td>
</tr>
</tbody>
</table>
<% }); %>
</tbody>
</table>
<div class="box"></div>
</div>
</main>
</body>

View File

@ -30,6 +30,8 @@
<h2>Liste des boites enregistrées</h2>
<div class="table">
<table>
<thead>
<tr>
@ -63,74 +65,83 @@
</tbody>
</table>
<div class="box"></div>
</div>
<h2>
Liste des pièces achetées
</h2>
<table>
<thead>
<tr>
<th>Id Pièce</th>
<th>Couleur</th>
<th>Motif</th>
<th>Forme</th>
<th>Quantité</th>
</tr>
</thead>
<tbody>
<% pieces.forEach(function(p) { %>
<div class="table">
<table>
<thead>
<tr>
<td>
<%=p.first.id_piece%>
</td>
<td>
<%=p.first.colour.name%> (<%=p.first.colour.id_colour%>)
</td>
<td>
<%=p.first.pattern.name%> (<%=p.first.pattern.id_pattern%>)
</td>
<td>
<%=p.first.shape.name%> (<%=p.first.shape.id_shape%>)
</td>
<td>
<%=p.second%>
</td>
<th>Id Pièce</th>
<th>Couleur</th>
<th>Motif</th>
<th>Forme</th>
<th>Quantité</th>
</tr>
</thead>
<tbody>
<% pieces.forEach(function(p) { %>
<tr>
<td>
<%=p.first.id_piece%>
</td>
<td>
<%=p.first.colour.name%> (<%=p.first.colour.id_colour%>)
</td>
<td>
<%=p.first.pattern.name%> (<%=p.first.pattern.id_pattern%>)
</td>
<td>
<%=p.first.shape.name%> (<%=p.first.shape.id_shape%>)
</td>
<td>
<%=p.second%>
</td>
</tr>
<% }); %>
</tbody>
</table>
<% }); %>
</tbody>
</table>
<div class="box"></div>
</div>
<h2>Liste des modèles faisables</h2>
<table>
<thead>
<tr>
<th>Id modèle</th>
<th>Nom modèle</th>
<th>Proposé par</th>
<th></th>
</tr>
</thead>
<tbody>
<% models.forEach(function(model) { %>
<div class="table">
<table>
<thead>
<tr>
<td>
<%=model.id%>
</td>
<td>
<%=model.name%>
</td>
<td><a href="/membres/byid/<%=model.creator%>"><i class="fa-solid fa-user"></i>
<%=model.creator%>
</a></td>
<td><a href="/models/byname/<%=model.name%>"><i class="fa-solid fa-eye"></i></a></td>
<th>Id modèle</th>
<th>Nom modèle</th>
<th>Proposé par</th>
<th></th>
</tr>
</thead>
<tbody>
<% models.forEach(function(model) { %>
<tr>
<td>
<%=model.id%>
</td>
<td>
<%=model.name%>
</td>
<td><a href="/membres/byid/<%=model.creator%>"><i class="fa-solid fa-user"></i>
<%=model.creator%>
</a></td>
<td><a href="/models/byname/<%=model.name%>"><i class="fa-solid fa-eye"></i></a></td>
</tr>
<% }); %>
<% }); %>
</tbody>
</table>
</body>
</tbody>
</table>
<div class="box"></div>
</div>
</main>
</body>
</html>

View File

@ -15,29 +15,39 @@
<%- include('partials/header.ejs') %>
<main>
<h1>
Liste des membres
</h1>
<table>
<thead>
<tr>
<th>Id Membre</th>
<th>Nom</th>
<th></th>
</tr>
</thead>
<tbody>
<h1>
Liste des membres
</h1>
<div class="table">
<table>
<thead>
<tr>
<th>Id Membre</th>
<th>Nom</th>
<th></th>
</tr>
</thead>
<tbody>
<% members.forEach(function(member) { %>
<tr>
<td>
<%=member.id_member%>
</td>
<td>
<%=member.name%>
</td>
<td><a href="/membres/byname/<%=member.name%>"><i class="fa-solid fa-eye"></i></a></td>
</tr>
<% }); %>
</tbody>
</table>
<div class="box"></div>
</div>
<% members.forEach(function(member) { %>
<tr>
<td><%=member.id_member%></td>
<td><%=member.name%></td>
<td><a href="/membres/byname/<%=member.name%>"><i class="fa-solid fa-eye"></i></a></td>
</tr>
<% }); %>
</tbody>
</table>
</main>

View File

@ -20,39 +20,41 @@
Liste des membres qui ont proposés le plus de modèles
</h1>
<table>
<thead>
<tr>
<th>Nom</th>
<th>Nombre de modèle proposés</th>
<th></th>
</tr>
<% members.forEach(function(member) { %>
<div class="table">
<table>
<thead>
<tr>
<td>
<a href="/membres/byid/<%=member.id_member%>">
<%= member.name %>
</a>
</td>
<td>
<%= member.nb_models %>
</td>
<td>
<a href="/membres/byid/<%=member.id_member%>">
<i class="fa-solid fa-eye"></i>
</a>
</td>
<th>Nom</th>
<th>Nombre de modèle proposés</th>
<th></th>
</tr>
<% }); %>
<% members.forEach(function(member) { %>
<tr>
<td>
<a href="/membres/byid/<%=member.id_member%>">
<%= member.name %>
</a>
</td>
<td>
<%= member.nb_models %>
</td>
<td>
<a href="/membres/byid/<%=member.id_member%>">
<i class="fa-solid fa-eye"></i>
</a>
</td>
</thead>
<tbody>
</tr>
<% }); %>
</thead>
<tbody>
</tbody>
</table>
<div class="box"></div>
</div>
</tbody>
</table>
<ul>
</ul>
</main>
</body>

View File

@ -26,22 +26,25 @@
creator : <%= model.creator.name %>
inheritFrom : <%= model.inheritFrom %>
<h2>Liste des pièces nécessaires</h2>
<table>
<thead>
<tr>
<th>Id pièce</th>
<th>Quantité pieces</th>
</tr>
</thead>
<tbody>
<% model.pieces.forEach(function(pair) { %>
<div class="table">
<table>
<thead>
<tr>
<td><a href="/pieces/<%=pair.first.id_piece%>"><i class="fa-solid fa-puzzle-piece"></i> <%= pair.first.id_piece%></a></td>
<td><%= pair.second%></td>
<th>Id pièce</th>
<th>Quantité pieces</th>
</tr>
<% }); %>
</tbody>
</table>
</thead>
<tbody>
<% model.pieces.forEach(function(pair) { %>
<tr>
<td><a href="/pieces/<%=pair.first.id_piece%>"><i class="fa-solid fa-puzzle-piece"></i> <%= pair.first.id_piece%></a></td>
<td><%= pair.second%></td>
</tr>
<% }); %>
</tbody>
</table>
<div class="box"></div>
</div>
</main>
</pre>

View File

@ -18,30 +18,33 @@
Liste des modeles
</h1>
<table>
<thead>
<tr>
<th>Id modèle</th>
<th>Nom modèle</th>
<th>Proposé par</th>
<th>Avis</th>
<th></th>
</tr>
</thead>
<tbody>
<% models.forEach(function(model) { %>
<tr>
<td><%=model.id%></td>
<td><%=model.name%></td>
<td><a href="/membres/byid/<%=model.creator.id_member%>"><i class="fa-solid fa-user"></i> <%=model.creator.name%></a></td>
<td><%=model.review.first%>/5 (<%=model.review.second%> avis)</td>
<td><a href="/models/byname/<%=model.name%>"><i class="fa-solid fa-eye"></i></a></td>
</tr>
<div class="table">
<table>
<thead>
<tr>
<th>Id modèle</th>
<th>Nom modèle</th>
<th>Proposé par</th>
<th>Avis</th>
<th></th>
</tr>
</thead>
<tbody>
<% models.forEach(function(model) { %>
<tr>
<td><%=model.id%></td>
<td><%=model.name%></td>
<td><a href="/membres/byid/<%=model.creator.id_member%>"><i class="fa-solid fa-user"></i> <%=model.creator.name%></a></td>
<td><%=model.review.first%>/5 (<%=model.review.second%> avis)</td>
<td><a href="/models/byname/<%=model.name%>"><i class="fa-solid fa-eye"></i></a></td>
</tr>
<% }); %>
<% }); %>
</tbody>
</table>
</tbody>
</table>
<div class="box"></div>
</div>
</main>

View File

@ -4,3 +4,6 @@
<link href="/static/css/header.css" rel="stylesheet" />
<link href="/static/css/table.css" rel="stylesheet" />
<link href="/static/css/reset.css" rel="stylesheet" />
<script type="text/javascript" src="/static/js/paginator.js"></script>
<script type="text/javascript" src="/static/js/table_pagination.js"></script>

View File

@ -18,24 +18,27 @@
<h1>
Information de la pièce
</h1>
<table>
<thead>
<tr>
<th>Id Pièce</th>
<th>Couleur</th>
<th>Motif</th>
<th>Forme</th>
</tr>
</thead>
<tbody>
<tr>
<td><%=piece.id_piece%></td>
<td><%=piece.colour.name%> (<%=piece.colour.id_colour%>)</td>
<td><%=piece.pattern.name%> (<%=piece.pattern.id_pattern%>)</td>
<td><%=piece.shape.name%> (<%=piece.shape.id_shape%>)</td>
</tr>
</tbody>
</table>
<div class="table">
<table>
<thead>
<tr>
<th>Id Pièce</th>
<th>Couleur</th>
<th>Motif</th>
<th>Forme</th>
</tr>
</thead>
<tbody>
<tr>
<td><%=piece.id_piece%></td>
<td><%=piece.colour.name%> (<%=piece.colour.id_colour%>)</td>
<td><%=piece.pattern.name%> (<%=piece.pattern.id_pattern%>)</td>
<td><%=piece.shape.name%> (<%=piece.shape.id_shape%>)</td>
</tr>
</tbody>
</table>
<div class="box"></div>
</div>
</main>

View File

@ -18,27 +18,32 @@
<h1>
Liste des pièces
</h1>
<table>
<thead>
<tr>
<th>Id Pièce</th>
<th>Couleur</th>
<th>Motif</th>
<th>Forme</th>
</tr>
</thead>
<tbody>
<% pieces.forEach(function(p) { %>
<tr>
<td><%=p.id_piece%></td>
<td><%=p.colour.name%> (<%=p.colour.id_colour%>)</td>
<td><%=p.pattern.name%> (<%=p.pattern.id_pattern%>)</td>
<td><%=p.shape.name%> (<%=p.shape.id_shape%>)</td>
</tr>
<div class="table">
<% }); %>
</tbody>
</table>
<table>
<thead>
<tr>
<th>Id Pièce</th>
<th>Couleur</th>
<th>Motif</th>
<th>Forme</th>
</tr>
</thead>
<tbody>
<% pieces.forEach(function(p) { %>
<tr>
<td><%=p.id_piece%></td>
<td><%=p.colour.name%> (<%=p.colour.id_colour%>)</td>
<td><%=p.pattern.name%> (<%=p.pattern.id_pattern%>)</td>
<td><%=p.shape.name%> (<%=p.shape.id_shape%>)</td>
</tr>
<% }); %>
</tbody>
</table>
<div class="box"></div>
</div>
</main>