fix pagination
This commit is contained in:
parent
ba83f29ab5
commit
e5bdbf5d8d
@ -246,16 +246,17 @@ 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)
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
Ok((num_pages, model_entities))
|
||||
}
|
||||
|
||||
|
@ -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()
|
||||
};
|
||||
|
@ -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*/),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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", ¬ifications);
|
||||
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()));
|
||||
|
@ -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">
|
||||
|
@ -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
|
||||
|
@ -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,96 +69,109 @@
|
||||
|
||||
<div id="{{ entity_name }}table">
|
||||
<form id="checked-rows">
|
||||
<table class="table 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 }}
|
||||
{% if sort_by == view_model.primary_key %}
|
||||
{% if sort_order == "asc" %}
|
||||
<i class="ml-1 fa-solid fa-caret-up"></i>
|
||||
{% else %}
|
||||
<i class="ml-1 fa-solid fa-caret-down"></i>
|
||||
{% endif %}
|
||||
{% 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 }}
|
||||
{% if sort_by == model_field.field_name %}
|
||||
<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 }}
|
||||
{% if sort_by == view_model.primary_key %}
|
||||
{% if sort_order == "asc" %}
|
||||
<i class="ml-1 fa-solid fa-caret-up"></i>
|
||||
<i class="ml-1 fa-solid fa-caret-up"></i>
|
||||
{% else %}
|
||||
<i class="ml-1 fa-solid fa-caret-down"></i>
|
||||
<i class="ml-1 fa-solid fa-caret-down"></i>
|
||||
{% endif %}
|
||||
{% 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 }}
|
||||
{% if sort_by == model_field.field_name %}
|
||||
{% if sort_order == "asc" %}
|
||||
<i class="ml-1 fa-solid fa-caret-up"></i>
|
||||
{% else %}
|
||||
<i class="ml-1 fa-solid fa-caret-down"></i>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
</th>
|
||||
{%- endfor %}
|
||||
<th>
|
||||
<!-- Edit Action -->
|
||||
<!-- Delete Action -->
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody hx-confirm="Are you sure?" hx-target="closest tr" hx-indicator="#loading" hx-swap="outerHTML">
|
||||
{% for entity in entities -%}
|
||||
<tr>
|
||||
<td><input type="checkbox" name="ids" value="{{ entity.primary_key }}"></td>
|
||||
<td>
|
||||
<a href="show/{{ entity.primary_key }}">
|
||||
<i class="fa-solid fa-magnifying-glass"></i> {{ entity.primary_key }}
|
||||
</a>
|
||||
</td>
|
||||
{% for model_field in view_model.fields -%}
|
||||
{% 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>
|
||||
{% else %}
|
||||
<td>{{ entity.values | get(key=model_field.field_name) }}</td>
|
||||
{% endif %}
|
||||
</th>
|
||||
{%- endfor %}
|
||||
<td>
|
||||
<a href="edit/{{ entity.primary_key }}"><i class="fa-solid fa-pen-to-square"></i></a>
|
||||
<a hx-delete="delete/{{ entity.primary_key }}"><i class="fa-solid fa-trash"></i></a>
|
||||
</td>
|
||||
</tr>
|
||||
{%- endfor %}
|
||||
<th>
|
||||
<!-- Edit Action -->
|
||||
<!-- Delete Action -->
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody hx-confirm="Are you sure?" hx-target="closest tr" hx-indicator="#loading" hx-swap="outerHTML">
|
||||
{% for entity in entities -%}
|
||||
<tr>
|
||||
<td><input type="checkbox" name="ids" value="{{ entity.primary_key }}"></td>
|
||||
<td>
|
||||
<a href="show/{{ entity.primary_key }}">
|
||||
<i class="fa-solid fa-magnifying-glass"></i> {{ entity.primary_key }}
|
||||
</a>
|
||||
</td>
|
||||
{% for model_field in view_model.fields -%}
|
||||
{% 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>
|
||||
{% else %}
|
||||
<td>{{ entity.values | get(key=model_field.field_name) }}</td>
|
||||
{% endif %}
|
||||
{%- endfor %}
|
||||
<td>
|
||||
<a href="edit/{{ entity.primary_key }}"><i class="fa-solid fa-pen-to-square"></i></a>
|
||||
<a hx-delete="delete/{{ entity.primary_key }}"><i class="fa-solid fa-trash"></i></a>
|
||||
</td>
|
||||
</tr>
|
||||
{%- endfor %}
|
||||
</tbody>
|
||||
<tfoot>
|
||||
<tr>
|
||||
<td colspan="{{ view_model.fields | length + 3 }}">
|
||||
</td>
|
||||
</tr>
|
||||
</tfoot>
|
||||
</table>
|
||||
</tbody>
|
||||
<tfoot>
|
||||
<tr>
|
||||
<td colspan="{{ view_model.fields | length + 3 }}">
|
||||
</td>
|
||||
</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">…</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">…</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
5
templates/loader.html
Normal file
@ -0,0 +1,5 @@
|
||||
<div id="loading" class="loader-wrapper htmx-indicator">
|
||||
<div>
|
||||
<div class="loader is-size-1"></div>
|
||||
</div>
|
||||
</div>
|
Loading…
Reference in New Issue
Block a user