initial filter implementation
This commit is contained in:
parent
de5d3826ed
commit
3095929d76
@ -35,6 +35,7 @@ sea-orm = { version = "^0.11.3", features = [], default-features = false }
|
|||||||
actix-admin-macros = { version = "0.4.0", path = "actix_admin_macros" }
|
actix-admin-macros = { version = "0.4.0", path = "actix_admin_macros" }
|
||||||
derive_more = "0.99.17"
|
derive_more = "0.99.17"
|
||||||
regex = "1.8.4"
|
regex = "1.8.4"
|
||||||
|
urlencoding = "2.1.2"
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
sea-orm = { version = "^0.11.3", features = [ "sqlx-sqlite", "runtime-actix-native-tls", "macros" ], default-features = true }
|
sea-orm = { version = "^0.11.3", features = [ "sqlx-sqlite", "runtime-actix-native-tls", "macros" ], default-features = true }
|
||||||
|
@ -66,15 +66,17 @@ pub fn derive_actix_admin_view_model(input: proc_macro::TokenStream) -> proc_mac
|
|||||||
entity_name: entity.table_name().to_string(),
|
entity_name: entity.table_name().to_string(),
|
||||||
fields: Entity::get_fields(),
|
fields: Entity::get_fields(),
|
||||||
show_search: #has_searchable_fields,
|
show_search: #has_searchable_fields,
|
||||||
user_can_access: None
|
user_can_access: None,
|
||||||
|
default_show_aside: Entity::get_filter().len() > 0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[actix_admin::prelude::async_trait(?Send)]
|
#[actix_admin::prelude::async_trait(?Send)]
|
||||||
impl ActixAdminViewModelTrait for Entity {
|
impl ActixAdminViewModelTrait for Entity {
|
||||||
async fn list(db: &DatabaseConnection, page: u64, entities_per_page: u64, search: &str, sort_by: &str, sort_order: &SortOrder) -> Result<(u64, Vec<ActixAdminModel>), ActixAdminError> {
|
async fn list(db: &DatabaseConnection, page: u64, entities_per_page: u64, viewmodel_filter: Vec<ActixAdminViewModelFilter>, search: &str, sort_by: &str, sort_order: &SortOrder) -> Result<(u64, Vec<ActixAdminModel>), ActixAdminError> {
|
||||||
let entities = Entity::list_model(db, page, entities_per_page, search, sort_by, sort_order).await;
|
let filter_values: HashMap<String, Option<String>> = viewmodel_filter.iter().map(|f| (f.name.to_string(), f.value.clone())).collect();
|
||||||
|
let entities = Entity::list_model(db, page, entities_per_page, filter_values, search, sort_by, sort_order).await;
|
||||||
entities
|
entities
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -96,6 +98,15 @@ pub fn derive_actix_admin_view_model(input: proc_macro::TokenStream) -> proc_mac
|
|||||||
Ok(model)
|
Ok(model)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async fn get_viewmodel_filter() -> HashMap<String, ActixAdminViewModelFilter> {
|
||||||
|
Entity::get_filter().iter().map(|f|
|
||||||
|
(f.name.to_string(), ActixAdminViewModelFilter {
|
||||||
|
name: f.name.to_string(),
|
||||||
|
value: None
|
||||||
|
})
|
||||||
|
).collect()
|
||||||
|
}
|
||||||
|
|
||||||
async fn get_entity(db: &DatabaseConnection, id: i32) -> Result<ActixAdminModel, ActixAdminError> {
|
async fn get_entity(db: &DatabaseConnection, id: i32) -> Result<ActixAdminModel, ActixAdminError> {
|
||||||
// TODO: separate primary key from other keys
|
// TODO: separate primary key from other keys
|
||||||
let entity = Entity::find_by_id(id).one(db).await?;
|
let entity = Entity::find_by_id(id).one(db).await?;
|
||||||
@ -281,7 +292,7 @@ pub fn derive_actix_admin_model(input: proc_macro::TokenStream) -> proc_macro::T
|
|||||||
|
|
||||||
#[actix_admin::prelude::async_trait]
|
#[actix_admin::prelude::async_trait]
|
||||||
impl ActixAdminModelTrait for Entity {
|
impl ActixAdminModelTrait for Entity {
|
||||||
async fn list_model(db: &DatabaseConnection, page: u64, posts_per_page: u64, search: &str, sort_by: &str, sort_order: &SortOrder) -> Result<(u64, Vec<ActixAdminModel>), ActixAdminError> {
|
async fn list_model(db: &DatabaseConnection, page: u64, posts_per_page: u64, filter_values: HashMap<String, Option<String>>, search: &str, sort_by: &str, sort_order: &SortOrder) -> Result<(u64, Vec<ActixAdminModel>), ActixAdminError> {
|
||||||
let sort_column = match sort_by {
|
let sort_column = match sort_by {
|
||||||
#(#fields_match_name_to_columns)*
|
#(#fields_match_name_to_columns)*
|
||||||
_ => panic!("Unknown column")
|
_ => panic!("Unknown column")
|
||||||
@ -301,6 +312,13 @@ pub fn derive_actix_admin_model(input: proc_macro::TokenStream) -> proc_macro::T
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let filters = Entity::get_filter();
|
||||||
|
for filter in filters {
|
||||||
|
let myfn = filter.filter;
|
||||||
|
let value = filter_values.get(&filter.name).unwrap_or_else(|| &None);
|
||||||
|
query = myfn(query, value.clone());
|
||||||
|
}
|
||||||
|
|
||||||
let paginator = query.paginate(db, posts_per_page);
|
let paginator = query.paginate(db, posts_per_page);
|
||||||
let num_pages = paginator.num_pages().await?;
|
let num_pages = paginator.num_pages().await?;
|
||||||
|
|
||||||
|
@ -48,4 +48,6 @@ impl ActixAdminModelValidationTrait<ActiveModel> for Entity {
|
|||||||
}
|
}
|
||||||
errors
|
errors
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl ActixAdminModelFilterTrait<Entity> for Entity {}
|
@ -67,4 +67,6 @@ impl FromStr for Tea {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ActixAdminModelValidationTrait<ActiveModel> for Entity {}
|
impl ActixAdminModelValidationTrait<ActiveModel> for Entity {}
|
||||||
|
|
||||||
|
impl ActixAdminModelFilterTrait<Entity> for Entity {}
|
@ -1,6 +1,6 @@
|
|||||||
use sea_orm::entity::prelude::*;
|
use sea_orm::entity::prelude::*;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use actix_admin::prelude::*;
|
use actix_admin::{prelude::*};
|
||||||
use super::Post;
|
use super::Post;
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Deserialize, Serialize, DeriveActixAdmin, DeriveActixAdminModel, DeriveActixAdminViewModel)]
|
#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Deserialize, Serialize, DeriveActixAdmin, DeriveActixAdminModel, DeriveActixAdminViewModel)]
|
||||||
@ -49,4 +49,24 @@ impl ActixAdminModelValidationTrait<ActiveModel> for Entity {
|
|||||||
|
|
||||||
errors
|
errors
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl ActixAdminModelFilterTrait<Entity> for Entity {
|
||||||
|
fn get_filter() -> Vec<ActixAdminModelFilter<Entity>> {
|
||||||
|
vec![
|
||||||
|
ActixAdminModelFilter::<Entity> {
|
||||||
|
name: "Id".to_string(),
|
||||||
|
filter: |q: sea_orm::Select<Entity>, v| -> sea_orm::Select<Entity> {
|
||||||
|
q.apply_if(v, | query, val: String| query.filter(Column::Id.eq(val)))
|
||||||
|
}
|
||||||
|
},
|
||||||
|
ActixAdminModelFilter::<Entity> {
|
||||||
|
name: "User".to_string(),
|
||||||
|
filter: |q: sea_orm::Select<Entity>, v| -> sea_orm::Select<Entity> {
|
||||||
|
q.apply_if(v, | query, val: String| query.filter(Column::User.eq(val)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@ -1,14 +1,15 @@
|
|||||||
// setup
|
// setup
|
||||||
use sea_orm::sea_query::{ForeignKeyCreateStatement, ColumnDef, TableCreateStatement};
|
use sea_orm::sea_query::{ColumnDef, ForeignKeyCreateStatement, TableCreateStatement};
|
||||||
use sea_orm::{Set, EntityTrait, error::*, sea_query, ConnectionTrait, DbConn, ExecResult };
|
use sea_orm::{
|
||||||
|
error::*, sea_query, ConnectionTrait, DbConn, EntityTrait, ExecResult, Set};
|
||||||
pub mod comment;
|
pub mod comment;
|
||||||
pub mod post;
|
pub mod post;
|
||||||
pub mod user;
|
pub mod user;
|
||||||
|
use chrono::{Duration, DurationRound, Local};
|
||||||
pub use comment::Entity as Comment;
|
pub use comment::Entity as Comment;
|
||||||
pub use post::Entity as Post;
|
pub use post::Entity as Post;
|
||||||
pub use user::Entity as User;
|
|
||||||
use chrono::{Local, Duration, DurationRound};
|
|
||||||
use sea_orm::prelude::Decimal;
|
use sea_orm::prelude::Decimal;
|
||||||
|
pub use user::Entity as User;
|
||||||
|
|
||||||
// setup
|
// setup
|
||||||
async fn create_table(db: &DbConn, stmt: &TableCreateStatement) -> Result<ExecResult, DbErr> {
|
async fn create_table(db: &DbConn, stmt: &TableCreateStatement) -> Result<ExecResult, DbErr> {
|
||||||
@ -29,7 +30,11 @@ pub async fn create_post_table(db: &DbConn) -> Result<ExecResult, DbErr> {
|
|||||||
)
|
)
|
||||||
.col(ColumnDef::new(post::Column::Title).string().not_null())
|
.col(ColumnDef::new(post::Column::Title).string().not_null())
|
||||||
.col(ColumnDef::new(post::Column::Text).string().not_null())
|
.col(ColumnDef::new(post::Column::Text).string().not_null())
|
||||||
.col(ColumnDef::new(post::Column::TeaMandatory).string().not_null())
|
.col(
|
||||||
|
ColumnDef::new(post::Column::TeaMandatory)
|
||||||
|
.string()
|
||||||
|
.not_null(),
|
||||||
|
)
|
||||||
.col(ColumnDef::new(post::Column::TeaOptional).string())
|
.col(ColumnDef::new(post::Column::TeaOptional).string())
|
||||||
.col(ColumnDef::new(post::Column::InsertDate).date().not_null())
|
.col(ColumnDef::new(post::Column::InsertDate).date().not_null())
|
||||||
.col(ColumnDef::new(post::Column::Attachment).string())
|
.col(ColumnDef::new(post::Column::Attachment).string())
|
||||||
@ -49,9 +54,21 @@ pub async fn create_post_table(db: &DbConn) -> Result<ExecResult, DbErr> {
|
|||||||
)
|
)
|
||||||
.col(ColumnDef::new(comment::Column::Comment).string().not_null())
|
.col(ColumnDef::new(comment::Column::Comment).string().not_null())
|
||||||
.col(ColumnDef::new(comment::Column::User).string().not_null())
|
.col(ColumnDef::new(comment::Column::User).string().not_null())
|
||||||
.col(ColumnDef::new(comment::Column::InsertDate).date_time().not_null())
|
.col(
|
||||||
.col(ColumnDef::new(comment::Column::IsVisible).boolean().not_null())
|
ColumnDef::new(comment::Column::InsertDate)
|
||||||
.col(ColumnDef::new(comment::Column::MyDecimal).decimal().not_null())
|
.date_time()
|
||||||
|
.not_null(),
|
||||||
|
)
|
||||||
|
.col(
|
||||||
|
ColumnDef::new(comment::Column::IsVisible)
|
||||||
|
.boolean()
|
||||||
|
.not_null(),
|
||||||
|
)
|
||||||
|
.col(
|
||||||
|
ColumnDef::new(comment::Column::MyDecimal)
|
||||||
|
.decimal()
|
||||||
|
.not_null(),
|
||||||
|
)
|
||||||
.col(ColumnDef::new(comment::Column::PostId).integer())
|
.col(ColumnDef::new(comment::Column::PostId).integer())
|
||||||
.foreign_key(
|
.foreign_key(
|
||||||
ForeignKeyCreateStatement::new()
|
ForeignKeyCreateStatement::new()
|
||||||
@ -82,35 +99,38 @@ pub async fn create_post_table(db: &DbConn) -> Result<ExecResult, DbErr> {
|
|||||||
|
|
||||||
for i in 1..1000 {
|
for i in 1..1000 {
|
||||||
let row = post::ActiveModel {
|
let row = post::ActiveModel {
|
||||||
title: Set(format!("Test {}", i)),
|
title: Set(format!("Test {}", i)),
|
||||||
text: Set("some content".to_string()),
|
text: Set("some content".to_string()),
|
||||||
tea_mandatory: Set(post::Tea::EverydayTea),
|
tea_mandatory: Set(post::Tea::EverydayTea),
|
||||||
tea_optional: Set(None),
|
tea_optional: Set(None),
|
||||||
insert_date: Set(Local::now().date_naive()),
|
insert_date: Set(Local::now().date_naive()),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
};
|
};
|
||||||
let _res = Post::insert(row).exec(db).await;
|
let _res = Post::insert(row).exec(db).await;
|
||||||
}
|
}
|
||||||
|
|
||||||
for i in 1..1000 {
|
for i in 1..1000 {
|
||||||
let row = comment::ActiveModel {
|
let row = comment::ActiveModel {
|
||||||
comment: Set(format!("Test {}", i)),
|
comment: Set(format!("Test {}", i)),
|
||||||
user: Set("me@home.com".to_string()),
|
user: Set("me@home.com".to_string()),
|
||||||
my_decimal: Set(Decimal::new(105, 0)),
|
my_decimal: Set(Decimal::new(105, 0)),
|
||||||
insert_date: Set(Local::now().naive_utc().duration_round(Duration::minutes(1)).unwrap()),
|
insert_date: Set(Local::now()
|
||||||
is_visible: Set(i%2 == 0),
|
.naive_utc()
|
||||||
..Default::default()
|
.duration_round(Duration::minutes(1))
|
||||||
};
|
.unwrap()),
|
||||||
let _res = Comment::insert(row).exec(db).await;
|
is_visible: Set(i % 2 == 0),
|
||||||
}
|
..Default::default()
|
||||||
|
};
|
||||||
|
let _res = Comment::insert(row).exec(db).await;
|
||||||
|
}
|
||||||
|
|
||||||
for i in 1..100 {
|
for i in 1..100 {
|
||||||
let row = user::ActiveModel {
|
let row = user::ActiveModel {
|
||||||
name: Set(format!("user {}", i)),
|
name: Set(format!("user {}", i)),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
};
|
};
|
||||||
let _res = User::insert(row).exec(db).await;
|
let _res = User::insert(row).exec(db).await;
|
||||||
}
|
}
|
||||||
|
|
||||||
_res
|
_res
|
||||||
}
|
}
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
use actix_admin::model::ActixAdminModelFilterTrait;
|
||||||
use sea_orm::entity::prelude::*;
|
use sea_orm::entity::prelude::*;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use actix_admin::prelude::*;
|
use actix_admin::prelude::*;
|
||||||
@ -71,4 +72,6 @@ impl FromStr for Tea {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ActixAdminModelValidationTrait<ActiveModel> for Entity {}
|
impl ActixAdminModelValidationTrait<ActiveModel> for Entity {}
|
||||||
|
|
||||||
|
impl ActixAdminModelFilterTrait<Entity> for Entity {}
|
@ -1,6 +1,6 @@
|
|||||||
use sea_orm::entity::prelude::*;
|
use sea_orm::entity::prelude::*;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use actix_admin::prelude::*;
|
use actix_admin::{prelude::*};
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Deserialize, Serialize, DeriveActixAdmin, DeriveActixAdminModel, DeriveActixAdminViewModel)]
|
#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Deserialize, Serialize, DeriveActixAdmin, DeriveActixAdminModel, DeriveActixAdminViewModel)]
|
||||||
#[sea_orm(table_name = "user")]
|
#[sea_orm(table_name = "user")]
|
||||||
@ -17,4 +17,6 @@ impl ActiveModelBehavior for ActiveModel {}
|
|||||||
#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
|
#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
|
||||||
pub enum Relation {}
|
pub enum Relation {}
|
||||||
|
|
||||||
impl ActixAdminModelValidationTrait<ActiveModel> for Entity {}
|
impl ActixAdminModelValidationTrait<ActiveModel> for Entity {}
|
||||||
|
|
||||||
|
impl ActixAdminModelFilterTrait<Entity> for Entity {}
|
@ -24,11 +24,11 @@ pub mod view_model;
|
|||||||
|
|
||||||
pub mod prelude {
|
pub mod prelude {
|
||||||
pub use crate::builder::{ActixAdminBuilder, ActixAdminBuilderTrait};
|
pub use crate::builder::{ActixAdminBuilder, ActixAdminBuilderTrait};
|
||||||
pub use crate::model::{ActixAdminModel, ActixAdminModelTrait, ActixAdminModelValidationTrait};
|
pub use crate::model::{ActixAdminModel, ActixAdminModelTrait, ActixAdminModelValidationTrait, ActixAdminModelFilter, ActixAdminModelFilterTrait};
|
||||||
pub use crate::routes::{create_or_edit_post, get_admin_ctx, SortOrder};
|
pub use crate::routes::{create_or_edit_post, get_admin_ctx, SortOrder};
|
||||||
pub use crate::view_model::{
|
pub use crate::view_model::{
|
||||||
ActixAdminViewModel, ActixAdminViewModelField, ActixAdminViewModelFieldType,
|
ActixAdminViewModel, ActixAdminViewModelField, ActixAdminViewModelFieldType,
|
||||||
ActixAdminViewModelSerializable, ActixAdminViewModelTrait,
|
ActixAdminViewModelSerializable, ActixAdminViewModelTrait, ActixAdminViewModelFilter
|
||||||
};
|
};
|
||||||
pub use crate::{hashmap, ActixAdminSelectListTrait};
|
pub use crate::{hashmap, ActixAdminSelectListTrait};
|
||||||
pub use crate::{ActixAdmin, ActixAdminAppDataTrait, ActixAdminConfiguration, ActixAdminError};
|
pub use crate::{ActixAdmin, ActixAdminAppDataTrait, ActixAdminConfiguration, ActixAdminError};
|
||||||
|
24
src/model.rs
24
src/model.rs
@ -1,11 +1,12 @@
|
|||||||
use crate::routes::SortOrder;
|
use crate::routes::SortOrder;
|
||||||
|
use crate::view_model::ActixAdminViewModelFilter;
|
||||||
use crate::{ActixAdminError, ActixAdminViewModelField};
|
use crate::{ActixAdminError, ActixAdminViewModelField};
|
||||||
use actix_multipart::{Multipart, MultipartError};
|
use actix_multipart::{Multipart, MultipartError};
|
||||||
use actix_web::web::Bytes;
|
use actix_web::web::Bytes;
|
||||||
use async_trait::async_trait;
|
use async_trait::async_trait;
|
||||||
use chrono::{NaiveDate, NaiveDateTime};
|
use chrono::{NaiveDate, NaiveDateTime};
|
||||||
use futures_util::stream::StreamExt as _;
|
use futures_util::stream::StreamExt as _;
|
||||||
use sea_orm::DatabaseConnection;
|
use sea_orm::{DatabaseConnection, EntityTrait};
|
||||||
use serde::Serialize;
|
use serde::Serialize;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::fs::File;
|
use std::fs::File;
|
||||||
@ -18,6 +19,7 @@ pub trait ActixAdminModelTrait {
|
|||||||
db: &DatabaseConnection,
|
db: &DatabaseConnection,
|
||||||
page: u64,
|
page: u64,
|
||||||
posts_per_page: u64,
|
posts_per_page: u64,
|
||||||
|
filter_values: HashMap<String, Option<String>>,
|
||||||
search: &str,
|
search: &str,
|
||||||
sort_by: &str,
|
sort_by: &str,
|
||||||
sort_order: &SortOrder
|
sort_order: &SortOrder
|
||||||
@ -32,6 +34,26 @@ pub trait ActixAdminModelValidationTrait<T> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub struct ActixAdminModelFilter<E: EntityTrait> {
|
||||||
|
pub name: String,
|
||||||
|
pub filter: fn(sea_orm::Select<E>, Option<String>) -> sea_orm::Select<E>
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait ActixAdminModelFilterTrait<E: EntityTrait> {
|
||||||
|
fn get_filter() -> Vec<ActixAdminModelFilter<E>> {
|
||||||
|
Vec::new()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: EntityTrait> From<ActixAdminModelFilter<T>> for ActixAdminViewModelFilter {
|
||||||
|
fn from(filter: ActixAdminModelFilter<T>) -> Self {
|
||||||
|
ActixAdminViewModelFilter {
|
||||||
|
name: filter.name,
|
||||||
|
value: None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, Serialize)]
|
#[derive(Clone, Debug, Serialize)]
|
||||||
pub struct ActixAdminModel {
|
pub struct ActixAdminModel {
|
||||||
pub primary_key: Option<String>,
|
pub primary_key: Option<String>,
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
use std::fmt;
|
use std::fmt;
|
||||||
|
use urlencoding::decode;
|
||||||
use crate::prelude::*;
|
use crate::prelude::*;
|
||||||
use actix_web::{error, web, Error, HttpRequest, HttpResponse};
|
use actix_web::{error, web, Error, HttpRequest, HttpResponse};
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
@ -81,7 +81,20 @@ pub async fn list<T: ActixAdminAppDataTrait, E: ActixAdminViewModelTrait>(
|
|||||||
.unwrap_or(view_model.primary_key.to_string());
|
.unwrap_or(view_model.primary_key.to_string());
|
||||||
let sort_order = params.sort_order.as_ref().unwrap_or(&SortOrder::Asc);
|
let sort_order = params.sort_order.as_ref().unwrap_or(&SortOrder::Asc);
|
||||||
|
|
||||||
let result = E::list(db, page, entities_per_page, &search, &sort_by, &sort_order).await;
|
let decoded_querystring = decode(req.query_string()).unwrap();
|
||||||
|
let actixadminfilters: Vec<ActixAdminViewModelFilter> = decoded_querystring
|
||||||
|
.split("&")
|
||||||
|
.filter(|qf| qf.starts_with("filter_"))
|
||||||
|
.map(|f| {
|
||||||
|
let mut kv = f.split("=");
|
||||||
|
let af = ActixAdminViewModelFilter {
|
||||||
|
name: kv.next().unwrap().strip_prefix("filter_").unwrap_or_default().to_string(),
|
||||||
|
value: kv.next().map(|s| s.to_string()).filter(|f| !f.is_empty()),
|
||||||
|
};
|
||||||
|
af
|
||||||
|
}).collect();
|
||||||
|
|
||||||
|
let result = E::list(db, page, entities_per_page, actixadminfilters, &search, &sort_by, &sort_order).await;
|
||||||
|
|
||||||
match result {
|
match result {
|
||||||
Ok(res) => {
|
Ok(res) => {
|
||||||
@ -130,6 +143,7 @@ pub async fn list<T: ActixAdminAppDataTrait, E: ActixAdminViewModelTrait>(
|
|||||||
ctx.insert("notifications", ¬ifications);
|
ctx.insert("notifications", ¬ifications);
|
||||||
ctx.insert("entities_per_page", &entities_per_page);
|
ctx.insert("entities_per_page", &entities_per_page);
|
||||||
ctx.insert("render_partial", &render_partial);
|
ctx.insert("render_partial", &render_partial);
|
||||||
|
ctx.insert("viewmodel_filter", &E::get_viewmodel_filter().await);
|
||||||
ctx.insert(
|
ctx.insert(
|
||||||
"view_model",
|
"view_model",
|
||||||
&ActixAdminViewModelSerializable::from(view_model.clone()),
|
&ActixAdminViewModelSerializable::from(view_model.clone()),
|
||||||
@ -138,7 +152,8 @@ pub async fn list<T: ActixAdminAppDataTrait, E: ActixAdminViewModelTrait>(
|
|||||||
ctx.insert("sort_by", &sort_by);
|
ctx.insert("sort_by", &sort_by);
|
||||||
ctx.insert("sort_order", &sort_order);
|
ctx.insert("sort_order", &sort_order);
|
||||||
|
|
||||||
let body = actix_admin.tera
|
let body = actix_admin
|
||||||
|
.tera
|
||||||
.render("list.html", &ctx)
|
.render("list.html", &ctx)
|
||||||
.map_err(|err| error::ErrorInternalServerError(err))?;
|
.map_err(|err| error::ErrorInternalServerError(err))?;
|
||||||
Ok(http_response_code.content_type("text/html").body(body))
|
Ok(http_response_code.content_type("text/html").body(body))
|
||||||
|
@ -27,7 +27,7 @@
|
|||||||
|
|
||||||
<div id="content">
|
<div id="content">
|
||||||
<div class="columns">
|
<div class="columns">
|
||||||
<aside id="nav_aside" class="column is-2 is-hidden is-narrow-mobile is-fullheight is-hidden-mobile">
|
<aside id="nav_aside" class="column is-2 {% if not view_model.default_show_aside %}is-hidden{% endif %} is-narrow-mobile is-fullheight is-hidden-mobile">
|
||||||
{% block aside %}
|
{% block aside %}
|
||||||
{% endblock aside %}
|
{% endblock aside %}
|
||||||
</aside>
|
</aside>
|
||||||
|
@ -80,11 +80,13 @@
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
let error = "<div class=\"notification mb-4 is-light is-danger\"><button class=\"delete\" onclick=\"this.parentElement.remove()\"></button>An Error occurred</div>";
|
||||||
htmx.on("htmx:responseError", function () {
|
htmx.on("htmx:responseError", function () {
|
||||||
document.getElementById("notifications").insertAdjacentHTML(
|
document.getElementById("notifications").insertAdjacentHTML("afterend", error)
|
||||||
"afterend",
|
});
|
||||||
"<div class=\"notification mb-4 is-light is-danger\"><button class=\"delete\" onclick=\"this.parentElement.remove()\"></button>An Error occurred</div>");
|
htmx.on("htmx:sendError", function () {
|
||||||
})
|
document.getElementById("notifications").insertAdjacentHTML("afterend", error)
|
||||||
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
|
@ -1,218 +1,239 @@
|
|||||||
{% extends "base.html" %}
|
{% extends "base.html" %}
|
||||||
|
|
||||||
{% block aside %}
|
{% block aside %}
|
||||||
<p class="menu-label is-hidden-touch">Filter</p>
|
<p class="menu-label is-hidden-touch">Filter</p>
|
||||||
|
<form id="filter_form" hx-indicator="#loading" hx-get="/admin/{{ entity_name }}/list"
|
||||||
|
hx-target="#{{ entity_name }}table" hx-push-url="true" hx-include="[id='{{ entity_name }}table']">
|
||||||
<ul class="menu-list">
|
<ul class="menu-list">
|
||||||
|
{% for key, value in viewmodel_filter %}
|
||||||
|
<li>
|
||||||
|
<div class="field">
|
||||||
|
<label class="label">{{key}}</label>
|
||||||
|
<div class="control">
|
||||||
|
<input class="input" type="text" placeholder="" name="filter_{{key}}">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
{% endfor %}
|
||||||
|
<li>
|
||||||
|
<div class="field mt-4 is-grouped">
|
||||||
|
<div class="control">
|
||||||
|
<button class="button is-link">Apply</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
|
</form>
|
||||||
{% endblock aside %}
|
{% endblock aside %}
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
|
|
||||||
{% if not render_partial or render_partial == false %}
|
{% if not render_partial or render_partial == false %}
|
||||||
<div class="column">
|
<div class="column">
|
||||||
<div class="columns">
|
<div class="columns">
|
||||||
<div class="column">
|
<div class="column">
|
||||||
<div class="buttons">
|
<div class="buttons">
|
||||||
<a class="button is-primary" href="/admin/{{ entity_name }}/create" hx-boost="true"
|
<a class="button is-primary" href="/admin/{{ entity_name }}/create" hx-boost="true"
|
||||||
hx-indicator="#loading"><i class="fa-solid fa-circle-plus"></i></a>
|
hx-indicator="#loading"><i class="fa-solid fa-circle-plus"></i></a>
|
||||||
|
{% if viewmodel_filter | length > 0 %}
|
||||||
<div class="dropdown mr-2 is-hoverable">
|
<button class="button" onclick="toggle_aside()"><i class="fa-solid fa-filter"></i></button>
|
||||||
<div class="dropdown-trigger">
|
{% endif %}
|
||||||
<button class="button" aria-haspopup="true" aria-controls="dropdown-menu4">
|
<div class="dropdown mr-2 is-hoverable">
|
||||||
<span><i class="fa-solid fa-list"></i></span>
|
<div class="dropdown-trigger">
|
||||||
<span class="icon is-small">
|
<button class="button" aria-haspopup="true" aria-controls="dropdown-menu4">
|
||||||
<i class="fas fa-angle-down" aria-hidden="true"></i>
|
<span><i class="fa-solid fa-list"></i></span>
|
||||||
</span>
|
<span class="icon is-small">
|
||||||
</button>
|
<i class="fas fa-angle-down" aria-hidden="true"></i>
|
||||||
</div>
|
</span>
|
||||||
<div class="dropdown-menu" id="dropdown-menu4">
|
</button>
|
||||||
<div class="dropdown-content">
|
</div>
|
||||||
<div class="dropdown-item">
|
<div class="dropdown-menu" id="dropdown-menu4">
|
||||||
<a hx-include="#table_form" hx-target="#{{ entity_name }}table" href="#"
|
<div class="dropdown-content">
|
||||||
hx-indicator="#loading" hx-confirm="Are you sure?" hx-delete="delete">Delete</a>
|
<div class="dropdown-item">
|
||||||
</div>
|
<a hx-include="#table_form" hx-target="#{{ entity_name }}table" href="#"
|
||||||
|
hx-indicator="#loading" hx-confirm="Are you sure?" hx-delete="delete">Delete</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<button class="button" onclick="toggle_aside()"><i class="fa-solid fa-filter"></i></button>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<form id="search_form" action="/admin/{{ entity_name }}/list" hx-boost="true" hx-indicator="#loading"
|
</div>
|
||||||
hx-target="#{{ entity_name }}table" hx-trigger="reload_table from:#entities_per_page">
|
<form id="search_form" action="/admin/{{ entity_name }}/list" hx-boost="true" hx-indicator="#loading"
|
||||||
<input type="hidden" id="sort_by" name="sort_by" value="{{ sort_by }}">
|
hx-target="#{{ entity_name }}table" hx-trigger="reload_table from:#entities_per_page" hx-include="[id='filter_form']">
|
||||||
<input type="hidden" id="sort_order" name="sort_order" value="{{ sort_order }}">
|
<input type="hidden" id="sort_by" name="sort_by" value="{{ sort_by }}">
|
||||||
<div class="column is-narrow">
|
<input type="hidden" id="sort_order" name="sort_order" value="{{ sort_order }}">
|
||||||
<div class="field is-horizontal">
|
<input type="hidden" name="search" value="{{ search }}">
|
||||||
{% if view_model.show_search %}
|
<input type="hidden" name="page" value="{{ page }}">
|
||||||
<p class="control has-icons-left has-icons-right">
|
<div class="column is-narrow">
|
||||||
<input class="input is-rounded" type="search" id="search" value="{{ search }}" name="search"
|
<div class="field is-horizontal">
|
||||||
placeholder="Search" hx-get="/admin/{{ entity_name }}/list"
|
{% if view_model.show_search %}
|
||||||
hx-trigger="keyup changed delay:500ms, search">
|
<p class="control has-icons-left has-icons-right">
|
||||||
<span class="icon is-small is-left">
|
<input class="input is-rounded" type="search" id="search" value="{{ search }}" name="search"
|
||||||
<i class="fas fa-search"></i>
|
placeholder="Search" hx-get="/admin/{{ entity_name }}/list"
|
||||||
</span>
|
hx-trigger="keyup changed delay:500ms, search">
|
||||||
</p>
|
<span class="icon is-small is-left">
|
||||||
{% endif %}
|
<i class="fas fa-search"></i>
|
||||||
<div class="select">
|
</span>
|
||||||
<div class="ml-1 control has-icons-left has-icons-right">
|
</p>
|
||||||
<select id="entities_per_page" class="select" name="entities_per_page"
|
{% endif %}
|
||||||
onchange="this.dispatchEvent(new Event('reload_table'));">
|
<div class="select">
|
||||||
{% for a in [10,20,50,100,] %}
|
<div class="ml-1 control has-icons-left has-icons-right">
|
||||||
<option {% if entities_per_page==a %}selected{% endif %} value="{{ a }}">{{ a }}
|
<select id="entities_per_page" class="select" name="entities_per_page"
|
||||||
</option>
|
onchange="this.dispatchEvent(new Event('reload_table'));">
|
||||||
{% endfor %}
|
{% for a in [10,20,50,100,] %}
|
||||||
</select>
|
<option {% if entities_per_page==a %}selected{% endif %} value="{{ a }}">{{ a }}
|
||||||
<p class="help">Entities per Page</p>
|
</option>
|
||||||
</div>
|
{% endfor %}
|
||||||
|
</select>
|
||||||
|
<p class="help">Entities per Page</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</div>
|
||||||
</div>
|
</form>
|
||||||
{% endif %}
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
<div id="{{ entity_name }}table">
|
<div id="{{ entity_name }}table">
|
||||||
<div class="is-relative">
|
<div class="is-relative">
|
||||||
{% include "loader.html" %}
|
{% include "loader.html" %}
|
||||||
<form id="table_form" hx-indicator="#loading" hx-get="/admin/{{ entity_name }}/list"
|
<form id="table_form" hx-indicator="#loading" hx-get="/admin/{{ entity_name }}/list"
|
||||||
hx-target="#{{ entity_name }}table">
|
hx-target="#{{ entity_name }}table" hx-include="[id='filter_form']">
|
||||||
<input type="hidden" id="sort_by" name="sort_by" value="{{ sort_by }}">
|
<input type="hidden" id="sort_by" name="sort_by" value="{{ sort_by }}">
|
||||||
<input type="hidden" id="sort_order" name="sort_order" value="{{ sort_order }}">
|
<input type="hidden" id="sort_order" name="sort_order" value="{{ sort_order }}">
|
||||||
<input type="hidden" name="entities_per_page" value="{{ entities_per_page }}">
|
<input type="hidden" name="entities_per_page" value="{{ entities_per_page }}">
|
||||||
<input type="hidden" name="search" value="{{ search }}">
|
<input type="hidden" name="search" value="{{ search }}">
|
||||||
<input type="hidden" name="page" value="{{ page }}">
|
<input type="hidden" name="page" value="{{ page }}">
|
||||||
<table class="table is-relative is-narrow is-fullwidth is-hoverable is-striped">
|
<table class="table is-relative is-narrow is-fullwidth is-hoverable is-striped">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th>
|
<th>
|
||||||
<input type="checkbox" onclick="checkAll(this)">
|
<input type="checkbox" onclick="checkAll(this)">
|
||||||
</th>
|
</th>
|
||||||
<th onclick="sort_by('{{ view_model.primary_key }}');" class="is-clickable">{{
|
<th onclick="sort_by('{{ view_model.primary_key }}');" class="is-clickable">{{
|
||||||
view_model.primary_key | title }}
|
view_model.primary_key | title }}
|
||||||
{% if sort_by == view_model.primary_key %}
|
{% if sort_by == view_model.primary_key %}
|
||||||
{% if sort_order == "Asc" %}
|
{% if sort_order == "Asc" %}
|
||||||
<i class="ml-1 fa-solid fa-caret-up"></i>
|
<i class="ml-1 fa-solid fa-caret-up"></i>
|
||||||
{% elif sort_order == "Desc" %}
|
{% elif sort_order == "Desc" %}
|
||||||
<i class="ml-1 fa-solid fa-caret-down"></i>
|
<i class="ml-1 fa-solid fa-caret-down"></i>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</th>
|
</th>
|
||||||
{% for model_field in view_model.fields | filter(attribute="list_hide_column",
|
{% for model_field in view_model.fields | filter(attribute="list_hide_column",
|
||||||
value=false) |
|
value=false) |
|
||||||
sort(attribute="list_sort_position") -%}
|
sort(attribute="list_sort_position") -%}
|
||||||
<th onclick="sort_by('{{ model_field.field_name }}');" class="is-clickable">{{
|
<th onclick="sort_by('{{ model_field.field_name }}');" class="is-clickable">{{
|
||||||
model_field.field_name | split(pat="_") | join(sep=" ") | title }}
|
model_field.field_name | split(pat="_") | join(sep=" ") | title }}
|
||||||
{% if sort_by == model_field.field_name %}
|
{% if sort_by == model_field.field_name %}
|
||||||
{% if sort_order == "Asc" %}
|
{% if sort_order == "Asc" %}
|
||||||
<i class="ml-1 fa-solid fa-caret-up"></i>
|
<i class="ml-1 fa-solid fa-caret-up"></i>
|
||||||
{% elif sort_order == "Desc" %}
|
{% elif sort_order == "Desc" %}
|
||||||
<i class="ml-1 fa-solid fa-caret-down"></i>
|
<i class="ml-1 fa-solid fa-caret-down"></i>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</th>
|
</th>
|
||||||
{%- endfor %}
|
{%- endfor %}
|
||||||
<th>
|
<th>
|
||||||
<!-- Edit Action -->
|
<!-- Edit Action -->
|
||||||
<!-- Delete Action -->
|
<!-- Delete Action -->
|
||||||
</th>
|
</th>
|
||||||
</tr>
|
</tr>
|
||||||
</form>
|
</form>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody hx-indicator="#loading" hx-boost="true">
|
<tbody hx-indicator="#loading" hx-boost="true">
|
||||||
{% for entity in entities -%}
|
{% for entity in entities -%}
|
||||||
<tr>
|
<tr>
|
||||||
<td><input type="checkbox" name="ids" value="{{ entity.primary_key }}"></td>
|
<td><input type="checkbox" name="ids" value="{{ entity.primary_key }}"></td>
|
||||||
<td>
|
<td>
|
||||||
<a href="/admin/{{ entity_name }}/show/{{ entity.primary_key }}" hx-vals='{
|
<a href="/admin/{{ entity_name }}/show/{{ entity.primary_key }}" hx-vals='{
|
||||||
"page" : "{{ page }}",
|
"page" : "{{ page }}",
|
||||||
"entities_per_page" : "{{ entities_per_page }}",
|
"entities_per_page" : "{{ entities_per_page }}",
|
||||||
"search" : "{{ search }}",
|
"search" : "{{ search }}",
|
||||||
"sort_by" : "{{ sort_by }}",
|
"sort_by" : "{{ sort_by }}",
|
||||||
"sort_order" : "{{ sort_order }}"
|
"sort_order" : "{{ sort_order }}"
|
||||||
}' hx-target="#content">
|
}' hx-target="#content">
|
||||||
<i class="fa-solid fa-magnifying-glass"></i> {{ entity.primary_key }}
|
<i class="fa-solid fa-magnifying-glass"></i> {{ entity.primary_key }}
|
||||||
</a>
|
</a>
|
||||||
</td>
|
</td>
|
||||||
{% for model_field in view_model.fields | filter(attribute="list_hide_column", value=false) |
|
{% for model_field in view_model.fields | filter(attribute="list_hide_column", value=false) |
|
||||||
sort(attribute="list_sort_position") -%}
|
sort(attribute="list_sort_position") -%}
|
||||||
{% if model_field.field_type == "Checkbox" %}
|
{% if model_field.field_type == "Checkbox" %}
|
||||||
<td>{{ entity.values | get(key=model_field.field_name) | get_icon | safe }}</td>
|
<td>{{ entity.values | get(key=model_field.field_name) | get_icon | safe }}</td>
|
||||||
{% elif model_field.field_type == "FileUpload" %}
|
{% elif model_field.field_type == "FileUpload" %}
|
||||||
<td><a href="file/{{ entity.primary_key }}/{{ model_field.field_name }}">{{
|
<td><a href="file/{{ entity.primary_key }}/{{ model_field.field_name }}">{{
|
||||||
entity.values
|
entity.values
|
||||||
| get(key=model_field.field_name) }}</a></td>
|
| get(key=model_field.field_name) }}</a></td>
|
||||||
{% else %}
|
{% else %}
|
||||||
<td>{{ entity.values | get(key=model_field.field_name) }}</td>
|
<td>{{ entity.values | get(key=model_field.field_name) }}</td>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{%- endfor %}
|
{%- endfor %}
|
||||||
<td class="has-text-right">
|
<td class="has-text-right">
|
||||||
<a hx-target="body" href="/admin/{{ entity_name }}/edit/{{ entity.primary_key }}" hx-vals='{
|
<a hx-target="body" href="/admin/{{ entity_name }}/edit/{{ entity.primary_key }}" hx-vals='{
|
||||||
"page" : "{{ page }}",
|
"page" : "{{ page }}",
|
||||||
"entities_per_page" : "{{ entities_per_page }}",
|
"entities_per_page" : "{{ entities_per_page }}",
|
||||||
"search" : "{{ search }}",
|
"search" : "{{ search }}",
|
||||||
"sort_by" : "{{ sort_by }}",
|
"sort_by" : "{{ sort_by }}",
|
||||||
"sort_order" : "{{ sort_order }}"
|
"sort_order" : "{{ sort_order }}"
|
||||||
}'>
|
}'>
|
||||||
<i class="fa-solid fa-pen-to-square"></i>
|
<i class="fa-solid fa-pen-to-square"></i>
|
||||||
</a>
|
</a>
|
||||||
<a hx-target="closest tr" hx-confirm="Are you sure?"
|
<a hx-target="closest tr" hx-confirm="Are you sure?"
|
||||||
hx-delete="delete/{{ entity.primary_key }}">
|
hx-delete="delete/{{ entity.primary_key }}">
|
||||||
<i class="fa-solid fa-trash"></i>
|
<i class="fa-solid fa-trash"></i>
|
||||||
</a>
|
</a>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
{%- endfor %}
|
{%- endfor %}
|
||||||
</tbody>
|
</tbody>
|
||||||
<tfoot>
|
<tfoot>
|
||||||
<tr>
|
<tr>
|
||||||
<td colspan="{{ view_model.fields | length + 3 }}">
|
<td colspan="{{ view_model.fields | length + 3 }}">
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
</tfoot>
|
</tfoot>
|
||||||
</table>
|
</table>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
<nav hx-boost="true" hx-push-url="true" hx-target="#{{ entity_name }}table" hx-vals='{
|
<nav hx-boost="true" hx-push-url="true" hx-target="#{{ entity_name }}table" hx-vals='{
|
||||||
"entities_per_page" : "{{ entities_per_page }}",
|
"entities_per_page" : "{{ entities_per_page }}",
|
||||||
"search" : "{{ search }}",
|
"search" : "{{ search }}",
|
||||||
"sort_by" : "{{ sort_by }}",
|
"sort_by" : "{{ sort_by }}",
|
||||||
"sort_order" : "{{ sort_order }}",
|
"sort_order" : "{{ sort_order }}",
|
||||||
"render_partial" : "true"
|
"render_partial" : "true"
|
||||||
}' hx-indicator="#loading" class="pagination is-rounded is-centered" role="pagination" aria-label="pagination">
|
}' hx-indicator="#loading" class="pagination is-rounded is-centered" role="pagination" aria-label="pagination">
|
||||||
{% if page > 1 %}
|
{% if page > 1 %}
|
||||||
<a href="/admin/{{ entity_name }}/list?&page={{ page - 1 }}"
|
<a href="/admin/{{ entity_name }}/list?&page={{ page - 1 }}" class="pagination-previous left-arrow-click"><i
|
||||||
class="pagination-previous left-arrow-click"><i class="fa-solid fa-arrow-left"></i>
|
class="fa-solid fa-arrow-left"></i>
|
||||||
|
</a>
|
||||||
|
{% endif %}
|
||||||
|
{% if page < num_pages %} <a href="/admin/{{ entity_name }}/list?page={{ page + 1 }}"
|
||||||
|
class="pagination-next right-arrow-click"><i class="fa-solid fa-arrow-right"></i>
|
||||||
</a>
|
</a>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if page < num_pages %} <a href="/admin/{{ entity_name }}/list?page={{ page + 1 }}"
|
<ul class="pagination-list">
|
||||||
class="pagination-next right-arrow-click"><i class="fa-solid fa-arrow-right"></i>
|
<li>
|
||||||
</a>
|
<a class="pagination-link {% if page == 1 %}is-current{% endif %}"
|
||||||
{% endif %}
|
href="/admin/{{ entity_name }}/list?page={{ 1 }}" aria-label="Goto page 1">1</a>
|
||||||
<ul class="pagination-list">
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<a class="pagination-link {% if page == 1 %}is-current{% endif %}"
|
<span class="pagination-ellipsis">…</span>
|
||||||
href="/admin/{{ entity_name }}/list?page={{ 1 }}" aria-label="Goto page 1">1</a>
|
</li>
|
||||||
</li>
|
{% for i in range(start=min_show_page,end=max_show_page) %}
|
||||||
<li>
|
<li><a class="pagination-link {% if page == i+1 %}is-current{% endif %}"
|
||||||
<span class="pagination-ellipsis">…</span>
|
aria-label="Goto page {{ i + 1 }}" href="/admin/{{ entity_name }}/list?page={{ i + 1 }}">{{
|
||||||
</li>
|
i + 1 }}</a></li>
|
||||||
{% for i in range(start=min_show_page,end=max_show_page) %}
|
{%- endfor %}
|
||||||
<li><a class="pagination-link {% if page == i+1 %}is-current{% endif %}"
|
<li>
|
||||||
aria-label="Goto page {{ i + 1 }}"
|
<span class="pagination-ellipsis">…</span>
|
||||||
href="/admin/{{ entity_name }}/list?page={{ i + 1 }}">{{
|
</li>
|
||||||
i + 1 }}</a></li>
|
<li>
|
||||||
{%- endfor %}
|
<a href="/admin/{{ entity_name }}/list?page={{ num_pages }}"
|
||||||
<li>
|
class="pagination-link is-rounded {% if page == num_pages %}is-current{% endif %}"
|
||||||
<span class="pagination-ellipsis">…</span>
|
aria-label="Goto page {{ num_pages }}">{{ num_pages }} </a>
|
||||||
</li>
|
</li>
|
||||||
<li>
|
</ul>
|
||||||
<a href="/admin/{{ entity_name }}/list?page={{ num_pages }}"
|
</div>
|
||||||
class="pagination-link is-rounded {% if page == num_pages %}is-current{% endif %}"
|
|
||||||
aria-label="Goto page {{ num_pages }}">{{ num_pages }} </a>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
</nav>
|
</nav>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@ -14,6 +14,7 @@ pub trait ActixAdminViewModelTrait {
|
|||||||
db: &DatabaseConnection,
|
db: &DatabaseConnection,
|
||||||
page: u64,
|
page: u64,
|
||||||
entities_per_page: u64,
|
entities_per_page: u64,
|
||||||
|
viewmodel_filter: Vec<ActixAdminViewModelFilter>,
|
||||||
search: &str,
|
search: &str,
|
||||||
sort_by: &str,
|
sort_by: &str,
|
||||||
sort_order: &SortOrder
|
sort_order: &SortOrder
|
||||||
@ -25,6 +26,7 @@ pub trait ActixAdminViewModelTrait {
|
|||||||
async fn get_entity(db: &DatabaseConnection, id: i32) -> Result<ActixAdminModel, ActixAdminError>;
|
async fn get_entity(db: &DatabaseConnection, id: i32) -> Result<ActixAdminModel, ActixAdminError>;
|
||||||
async fn edit_entity(db: &DatabaseConnection, id: i32, model: ActixAdminModel) -> Result<ActixAdminModel, ActixAdminError>;
|
async fn edit_entity(db: &DatabaseConnection, id: i32, model: ActixAdminModel) -> Result<ActixAdminModel, ActixAdminError>;
|
||||||
async fn get_select_lists(db: &DatabaseConnection) -> Result<HashMap<String, Vec<(String, String)>>, ActixAdminError>;
|
async fn get_select_lists(db: &DatabaseConnection) -> Result<HashMap<String, Vec<(String, String)>>, ActixAdminError>;
|
||||||
|
async fn get_viewmodel_filter() -> HashMap<String, ActixAdminViewModelFilter>;
|
||||||
fn validate_entity(model: &mut ActixAdminModel);
|
fn validate_entity(model: &mut ActixAdminModel);
|
||||||
|
|
||||||
fn get_entity_name() -> String;
|
fn get_entity_name() -> String;
|
||||||
@ -40,7 +42,8 @@ pub struct ActixAdminViewModel {
|
|||||||
pub primary_key: String,
|
pub primary_key: String,
|
||||||
pub fields: &'static[ActixAdminViewModelField],
|
pub fields: &'static[ActixAdminViewModelField],
|
||||||
pub show_search: bool,
|
pub show_search: bool,
|
||||||
pub user_can_access: Option<fn(&Session) -> bool>
|
pub user_can_access: Option<fn(&Session) -> bool>,
|
||||||
|
pub default_show_aside: bool
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, Serialize)]
|
#[derive(Clone, Debug, Serialize)]
|
||||||
@ -48,7 +51,14 @@ pub struct ActixAdminViewModelSerializable {
|
|||||||
pub entity_name: String,
|
pub entity_name: String,
|
||||||
pub primary_key: String,
|
pub primary_key: String,
|
||||||
pub fields: &'static [ActixAdminViewModelField],
|
pub fields: &'static [ActixAdminViewModelField],
|
||||||
pub show_search: bool
|
pub show_search: bool,
|
||||||
|
pub default_show_aside: bool
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Serialize)]
|
||||||
|
pub struct ActixAdminViewModelFilter {
|
||||||
|
pub name: String,
|
||||||
|
pub value: Option<String>
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: better alternative to serialize only specific fields for ActixAdminViewModel
|
// TODO: better alternative to serialize only specific fields for ActixAdminViewModel
|
||||||
@ -58,12 +68,12 @@ impl From<ActixAdminViewModel> for ActixAdminViewModelSerializable {
|
|||||||
entity_name: entity.entity_name,
|
entity_name: entity.entity_name,
|
||||||
primary_key: entity.primary_key,
|
primary_key: entity.primary_key,
|
||||||
fields: entity.fields,
|
fields: entity.fields,
|
||||||
show_search: entity.show_search
|
show_search: entity.show_search,
|
||||||
|
default_show_aside: entity.default_show_aside
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
|
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
|
||||||
pub enum ActixAdminViewModelFieldType {
|
pub enum ActixAdminViewModelFieldType {
|
||||||
Number,
|
Number,
|
||||||
|
Loading…
Reference in New Issue
Block a user