fix pagination

This commit is contained in:
manuel 2023-01-08 15:45:28 +01:00
parent ba83f29ab5
commit e5bdbf5d8d
8 changed files with 140 additions and 91 deletions

View File

@ -246,10 +246,11 @@ pub fn derive_actix_admin_model(input: proc_macro::TokenStream) -> proc_macro::T
.order_by_asc(Column::Id)
.paginate(db, posts_per_page);
let num_pages = paginator.num_pages().await?;
let entities = paginator
.fetch_page(page - 1)
.await?;
let mut model_entities = Vec::new();
if (num_pages == 0) { return Ok((num_pages, model_entities)) };
let entities = paginator
.fetch_page(std::cmp::min(num_pages - 1, page - 1))
.await?;
for entity in entities {
model_entities.push(
ActixAdminModel::from(entity)

View File

@ -5,7 +5,7 @@ pub mod comment;
pub mod post;
pub use comment::Entity as Comment;
pub use post::Entity as Post;
use sea_orm::prelude::DateTime;
use chrono::{Local};
use sea_orm::prelude::Decimal;
// setup
@ -29,7 +29,7 @@ pub async fn create_post_table(db: &DbConn) -> Result<ExecResult, DbErr> {
.col(ColumnDef::new(post::Column::Text).string().not_null())
.col(ColumnDef::new(post::Column::TeaMandatory).string().not_null())
.col(ColumnDef::new(post::Column::TeaOptional).string())
.col(ColumnDef::new(post::Column::InsertDate).date())
.col(ColumnDef::new(post::Column::InsertDate).date().not_null())
.col(ColumnDef::new(post::Column::Attachment).string())
.to_owned();
@ -63,23 +63,24 @@ pub async fn create_post_table(db: &DbConn) -> Result<ExecResult, DbErr> {
let res = create_table(db, &stmt).await;
for i in 1..101 {
for i in 1..1000 {
let row = post::ActiveModel {
title: Set(format!("Test {}", i)),
text: Set("some content".to_string()),
tea_mandatory: Set(post::Tea::EverydayTea),
tea_optional: Set(None),
insert_date: Set(Local::now().date_naive()),
..Default::default()
};
let _res = Post::insert(row).exec(db).await;
}
for i in 1..101 {
for i in 1..1000 {
let row = comment::ActiveModel {
comment: Set(format!("Test {}", i)),
user: Set("me@home.com".to_string()),
my_decimal: Set(Decimal::new(105, 0)),
insert_date: Set(DateTime::default()),
insert_date: Set(Local::now().naive_utc()),
is_visible: Set(i%2 == 0),
..Default::default()
};

View File

@ -21,15 +21,16 @@ pub struct Model {
pub tea_mandatory: Tea,
#[actix_admin(select_list="Tea")]
pub tea_optional: Option<Tea>,
#[sea_orm(column_type = "Date")]
pub insert_date: Date,
#[actix_admin(file_upload)]
pub attachment: String
pub attachment: Option<String>
}
impl Display for Model {
fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
match &*self {
_ => write!(formatter, "{} {}", &self.title, &self.insert_date),
_ => write!(formatter, "{} {}", &self.title, ""/* &self.insert_date*/),
}
}
}

View File

@ -44,7 +44,7 @@ pub async fn list<T: ActixAdminAppDataTrait, E: ActixAdminViewModelTrait>(
let params = web::Query::<Params>::from_query(req.query_string()).unwrap();
let page = params.page.unwrap_or(1);
let mut page = params.page.unwrap_or(1);
let entities_per_page = params
.entities_per_page
.unwrap_or(DEFAULT_ENTITIES_PER_PAGE);
@ -58,13 +58,22 @@ pub async fn list<T: ActixAdminAppDataTrait, E: ActixAdminViewModelTrait>(
match result {
Ok(res) => {
let entities = res.1;
let num_pages = res.0;
let num_pages = std::cmp::max(res.0, 1);
ctx.insert("entities", &entities);
ctx.insert("num_pages", &num_pages);
ctx.insert("page", &std::cmp::min(num_pages, page));
page = std::cmp::min(page, num_pages);
let min_show_page = if &page < &5 { 1 } else { let max_page = &page - &5; max_page };
let max_show_page = if &page >= &num_pages { std::cmp::max(1, num_pages - 1) } else { let max_page = &page + &5; std::cmp::min(num_pages - 1, max_page) };
ctx.insert("min_show_page", &min_show_page);
ctx.insert("max_show_page", &max_show_page);
},
Err(e) => {
ctx.insert("entities", &Vec::<ActixAdminModel>::new());
ctx.insert("num_pages", &0);
ctx.insert("min_show_page", &1);
ctx.insert("max_show_page", &1);
ctx.insert("page", &1);
errors.push(e);
}
}
@ -79,7 +88,6 @@ pub async fn list<T: ActixAdminAppDataTrait, E: ActixAdminViewModelTrait>(
ctx.insert("entity_name", &entity_name);
ctx.insert("notifications", &notifications);
ctx.insert("page", &page);
ctx.insert("entities_per_page", &entities_per_page);
ctx.insert("render_partial", &render_partial);
ctx.insert("view_model", &ActixAdminViewModelSerializable::from(view_model.clone()));

View File

@ -12,9 +12,6 @@
</head>
<body>
<div id="loading" class="loader-wrapper htmx-indicator">
<div class="loader is-size-1"></div>
</div>
{% include "navbar.html" %}
<div class="container is-fluid">
<div id="notifications">

View File

@ -7,6 +7,29 @@
<script src="https://unpkg.com/htmx.org@1.8.4"></script>
<script>
document.onkeydown = function (e) {
switch (e.which) {
case 37: // left
let left_el = document.getElementsByClassName('left-arrow-click').item(0);
if (left_el) { left_el.click(); };
break;
//case 38: // up
// break;
case 39: // right
let right_el = document.getElementsByClassName('right-arrow-click').item(0);
if (right_el) { right_el.click(); };
break;
//case 40: // down
// break;
default: return; // exit this handler for other keys
}
e.preventDefault(); // prevent the default action (scroll / move caret)
};
function checkAll(bx) {
var cbs = document.getElementsByTagName('input');
for (var i = 0; i < cbs.length; i++) {
@ -19,7 +42,7 @@
function sort_by(column) {
document.getElementById("sort_by").value = column;
current_sort_order = document.getElementById("sort_order").value;
if(current_sort_order == "asc") {
if (current_sort_order == "asc") {
document.getElementById("sort_order").value = "desc";
} else {
document.getElementById("sort_order").value = "asc";
@ -57,13 +80,12 @@
<style>
.loader-wrapper {
position: absolute;
top: 0;
left: 0;
height: 100%;
width: 100%;
display: flex;
background: rgba(255, 255, 255, 0.3);
justify-content: center;
border-radius: 6px;
align-items: center;
z-index: 6;
pointer-events: none

View File

@ -33,6 +33,7 @@
<p class="control has-icons-left has-icons-right">
<input class="input is-rounded" type="search" id="search" value="{{ search }}" name="search"
placeholder="Search"
hx-push-url="true"
hx-get="/admin/{{ entity_name }}/list?render_partial=true&entities_per_page={{ entities_per_page }}&page={{ page }}&sort_by={{ sort_by }}&sort_order={{ sort_order }}"
hx-trigger="keyup changed delay:500ms, search" hx-target="#{{ entity_name }}table"
hx-indicator="#loading">
@ -68,13 +69,16 @@
<div id="{{ entity_name }}table">
<form id="checked-rows">
<table class="table is-fullwidth is-hoverable is-striped">
<div class="is-relative">
{% include "loader.html" %}
<table class="table is-relative is-fullwidth is-hoverable is-striped">
<thead>
<tr>
<th>
<input type="checkbox" onclick="checkAll(this)">
</th>
<th onclick="sort_by('{{ view_model.primary_key }}');" class="is-clickable">{{ view_model.primary_key | title }}
<th onclick="sort_by('{{ view_model.primary_key }}');" class="is-clickable">{{
view_model.primary_key | title }}
{% if sort_by == view_model.primary_key %}
{% if sort_order == "asc" %}
<i class="ml-1 fa-solid fa-caret-up"></i>
@ -84,7 +88,8 @@
{% endif %}
</th>
{% for model_field in view_model.fields -%}
<th onclick="sort_by('{{ model_field.field_name }}');" class="is-clickable">{{ model_field.field_name | split(pat="_") | join(sep=" ") | title }}
<th onclick="sort_by('{{ model_field.field_name }}');" class="is-clickable">{{
model_field.field_name | split(pat="_") | join(sep=" ") | title }}
{% if sort_by == model_field.field_name %}
{% if sort_order == "asc" %}
<i class="ml-1 fa-solid fa-caret-up"></i>
@ -113,7 +118,9 @@
{% if model_field.field_type == "Checkbox" %}
<td>{{ entity.values | get(key=model_field.field_name) | get_icon | safe }}</td>
{% elif model_field.field_type == "FileUpload" %}
<td><a href="static_content/{{ entity.primary_key }}/{{ model_field.field_name }}">{{ entity.values | get(key=model_field.field_name) }}</a></td>
<td><a href="static_content/{{ entity.primary_key }}/{{ model_field.field_name }}">{{
entity.values
| get(key=model_field.field_name) }}</a></td>
{% else %}
<td>{{ entity.values | get(key=model_field.field_name) }}</td>
{% endif %}
@ -132,32 +139,39 @@
</tr>
</tfoot>
</table>
</div>
</form>
<nav hx-boost="true" hx-indicator="#loading" class="pagination" aria-label="pagination">
<nav hx-boost="true" hx-indicator="#loading" class="pagination is-rounded is-centered" role="pagination" aria-label="pagination">
{% if page > 1 %}
<a href="?page={{ page - 1 }}&entities_per_page={{ entities_per_page }}&search={{ search }}"
class="pagination-previous">Previous Page</a>
class="pagination-previous left-arrow-click"><i class="fa-solid fa-arrow-left"></i>
</a>
{% endif %}
{% if page < num_pages %}
<a href="?page={{ page + 1 }}&entities_per_page={{ entities_per_page }}&search={{ search }}"
class="pagination-next">Next page</a>
class="pagination-next right-arrow-click"><i class="fa-solid fa-arrow-right"></i>
</a>
{% endif %}
<ul class="pagination-list">
<!-- <li>
<a class="pagination-link"
href="?page={{ page - 1 }}&entities_per_page={{ entities_per_page }}&search={{ search }}"
<li>
<a class="pagination-link {% if page == 1 %}is-current{% endif %}"
href="?page={{ 1 }}&entities_per_page={{ entities_per_page }}&search={{ search }}"
aria-label="Goto page 1">1</a>
</li> -->
</li>
<li>
<span class="pagination-ellipsis">&hellip;</span>
</li>
{% for i in range(end=num_pages) %}
<li><a class="pagination-link" aria-label="Goto page 45"
{% for i in range(start=min_show_page,end=max_show_page) %}
<li><a class="pagination-link {% if page == i+1 %}is-current{% endif %}" aria-label="Goto page {{ i + 1 }}"
href="?page={{ i + 1 }}&entities_per_page={{ entities_per_page }}&search={{ search }}">{{
i + 1 }}</a></li>
{%- endfor %}
<li>
<span class="pagination-ellipsis">&hellip;</span>
</li>
<!-- <li>
<a class="pagination-link" aria-label="Goto page {{ num_pages }}">{{ num_pages }} </a>
</li> -->
<li>
<a href="?page={{ num_pages }}&entities_per_page={{ entities_per_page }}&search={{ search }}" class="pagination-link is-rounded {% if page == num_pages %}is-current{% endif %}" aria-label="Goto page {{ num_pages }}">{{ num_pages }} </a>
</li>
</ul>
</nav>
</div>

5
templates/loader.html Normal file
View File

@ -0,0 +1,5 @@
<div id="loading" class="loader-wrapper htmx-indicator">
<div>
<div class="loader is-size-1"></div>
</div>
</div>