add dropdown for custom filters
This commit is contained in:
parent
cf9b8f0c26
commit
d95c84c2c6
@ -2,7 +2,7 @@
|
||||
name = "actix-admin"
|
||||
description = "An admin interface for actix-web"
|
||||
license = "MIT OR Apache-2.0"
|
||||
version = "0.4.0"
|
||||
version = "0.5.0"
|
||||
repository = "https://github.com/mgugger/actix-admin"
|
||||
edition = "2021"
|
||||
exclude = [
|
||||
@ -32,7 +32,7 @@ itertools = "^0.10.5"
|
||||
serde = "^1.0.164"
|
||||
serde_derive = "^1.0.164"
|
||||
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.5.0", path = "actix_admin_macros" }
|
||||
derive_more = "0.99.17"
|
||||
regex = "1.8.4"
|
||||
urlencoding = "2.1.2"
|
||||
|
@ -3,7 +3,7 @@ name = "actix-admin-macros"
|
||||
description = "macros to be used with actix-admin crate"
|
||||
license = "MIT OR Apache-2.0"
|
||||
repository = "https://github.com/mgugger/actix-admin"
|
||||
version = "0.4.0"
|
||||
version = "0.5.0"
|
||||
edition = "2021"
|
||||
exclude = [
|
||||
"tests/*"
|
||||
|
@ -98,13 +98,22 @@ pub fn derive_actix_admin_view_model(input: proc_macro::TokenStream) -> proc_mac
|
||||
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_viewmodel_filter(db: &DatabaseConnection) -> HashMap<String, ActixAdminViewModelFilter> {
|
||||
let mut hashmap: HashMap<String, ActixAdminViewModelFilter> = HashMap::new();
|
||||
|
||||
for filter in Entity::get_filter() {
|
||||
hashmap.insert(
|
||||
filter.name.to_string(),
|
||||
ActixAdminViewModelFilter {
|
||||
name: filter.name.to_string(),
|
||||
value: None,
|
||||
values: Entity::get_filter_values(&filter, db).await,
|
||||
filter_type: Some(filter.filter_type)
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
hashmap
|
||||
}
|
||||
|
||||
async fn get_entity(db: &DatabaseConnection, id: i32) -> Result<ActixAdminModel, ActixAdminError> {
|
||||
|
@ -24,7 +24,7 @@ Check the [examples](https://github.com/mgugger/actix-admin/tree/main/examples)
|
||||
[package]
|
||||
name = "actix-admin-example"
|
||||
description = "An admin interface for actix-web"
|
||||
version = "0.4.0"
|
||||
version = "0.5.0"
|
||||
edition = "2021"
|
||||
|
||||
[[bin]]
|
||||
@ -40,6 +40,6 @@ chrono = "0.4.23"
|
||||
tera = "^1.17.1"
|
||||
serde = "^1.0.152"
|
||||
serde_derive = "^1.0.152"
|
||||
actix-admin = { version = "0.4.0", path = "../../" }
|
||||
actix-admin = { version = "0.5.0", path = "../../" }
|
||||
regex = "1.7.1"
|
||||
```
|
8
docs/content/docs/custom-filters.md
Normal file
8
docs/content/docs/custom-filters.md
Normal file
@ -0,0 +1,8 @@
|
||||
---
|
||||
title: "Custom Filters"
|
||||
date: 2023-01-17T11:44:56+01:00
|
||||
draft: false
|
||||
weight: 6
|
||||
---
|
||||
|
||||
# Custom Filters
|
@ -12,7 +12,7 @@ weight: 1
|
||||
Cargo.toml:
|
||||
```cargo
|
||||
[dependencies]
|
||||
actix-admin = "0.4.0"
|
||||
actix-admin = "0.5.0"
|
||||
```
|
||||
|
||||
## Build the Actix-Admin Configuration
|
||||
@ -50,7 +50,7 @@ let app = App::new()
|
||||
.app_data(web::Data::new(conn.clone()))
|
||||
.app_data(web::Data::new(actix_admin_builder.get_actix_admin()))
|
||||
.service(
|
||||
actix_admin_builder.get_scope::<AppState>()
|
||||
actix_admin_builder.get_scope()
|
||||
)
|
||||
.wrap(middleware::Logger::default())
|
||||
```
|
||||
|
@ -1,7 +1,7 @@
|
||||
use sea_orm::entity::prelude::*;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use actix_admin::{prelude::*};
|
||||
use super::Post;
|
||||
use super::{Post, post};
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Deserialize, Serialize, DeriveActixAdmin, DeriveActixAdminModel, DeriveActixAdminViewModel)]
|
||||
#[sea_orm(table_name = "comment")]
|
||||
@ -51,22 +51,61 @@ impl ActixAdminModelValidationTrait<ActiveModel> for Entity {
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
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_type: ActixAdminModelFilterType::Text,
|
||||
filter: |q: sea_orm::Select<Entity>, v| -> sea_orm::Select<Entity> {
|
||||
q.apply_if(v, | query, val: String| query.filter(Column::User.eq(val)))
|
||||
}
|
||||
},
|
||||
values: None
|
||||
},
|
||||
ActixAdminModelFilter::<Entity> {
|
||||
name: "Insert Date After".to_string(),
|
||||
filter_type: ActixAdminModelFilterType::DateTime,
|
||||
filter: |q: sea_orm::Select<Entity>, v| -> sea_orm::Select<Entity> {
|
||||
q.apply_if(v, | query, val: String| query.filter(Column::InsertDate.gte(val)))
|
||||
},
|
||||
values: None
|
||||
},
|
||||
ActixAdminModelFilter::<Entity> {
|
||||
name: "Insert Date After".to_string(),
|
||||
filter_type: ActixAdminModelFilterType::DateTime,
|
||||
filter: |q: sea_orm::Select<Entity>, v| -> sea_orm::Select<Entity> {
|
||||
q.apply_if(v, | query, val: String| query.filter(Column::InsertDate.gte(val)))
|
||||
},
|
||||
values: None
|
||||
},
|
||||
ActixAdminModelFilter::<Entity> {
|
||||
name: "Is Visible".to_string(),
|
||||
filter_type: ActixAdminModelFilterType::Checkbox,
|
||||
filter: |q: sea_orm::Select<Entity>, v| -> sea_orm::Select<Entity> {
|
||||
q.apply_if(v, | query, val: String| query.filter(Column::IsVisible.eq(val)))
|
||||
},
|
||||
values: None
|
||||
},
|
||||
ActixAdminModelFilter::<Entity> {
|
||||
name: "Post".to_string(),
|
||||
filter_type: ActixAdminModelFilterType::SelectList,
|
||||
filter: |q: sea_orm::Select<Entity>, v| -> sea_orm::Select<Entity> {
|
||||
q.apply_if(v, | query, val: String| query.filter(Column::PostId.eq(val)))
|
||||
},
|
||||
values: None
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
async fn get_filter_values(filter: &ActixAdminModelFilter<Entity>, db: &DatabaseConnection) -> Option<Vec<(String, String)>> {
|
||||
match filter.name.as_str() {
|
||||
"Post" => Some({
|
||||
Post::find().order_by_asc(post::Column::Id).all(db).await.unwrap()
|
||||
.iter().map(|p| (p.id.to_string(), p.title.to_string())).collect()
|
||||
}),
|
||||
_ => None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -24,7 +24,7 @@ pub mod view_model;
|
||||
|
||||
pub mod prelude {
|
||||
pub use crate::builder::{ActixAdminBuilder, ActixAdminBuilderTrait};
|
||||
pub use crate::model::{ActixAdminModel, ActixAdminModelTrait, ActixAdminModelValidationTrait, ActixAdminModelFilter, ActixAdminModelFilterTrait};
|
||||
pub use crate::model::{ActixAdminModel, ActixAdminModelTrait, ActixAdminModelValidationTrait, ActixAdminModelFilter, ActixAdminModelFilterTrait, ActixAdminModelFilterType};
|
||||
pub use crate::routes::{create_or_edit_post, get_admin_ctx, SortOrder};
|
||||
pub use crate::view_model::{
|
||||
ActixAdminViewModel, ActixAdminViewModelField, ActixAdminViewModelFieldType,
|
||||
|
21
src/model.rs
21
src/model.rs
@ -36,20 +36,37 @@ 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 filter_type: ActixAdminModelFilterType,
|
||||
pub filter: fn(sea_orm::Select<E>, Option<String>) -> sea_orm::Select<E>,
|
||||
pub values: Option<Vec<(String, String)>>
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize)]
|
||||
pub enum ActixAdminModelFilterType {
|
||||
Text,
|
||||
SelectList,
|
||||
Date,
|
||||
DateTime,
|
||||
Checkbox
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
pub trait ActixAdminModelFilterTrait<E: EntityTrait> {
|
||||
fn get_filter() -> Vec<ActixAdminModelFilter<E>> {
|
||||
Vec::new()
|
||||
}
|
||||
async fn get_filter_values(_filter: &ActixAdminModelFilter<E>, _db: &DatabaseConnection)-> Option<Vec<(String, String)>> {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: EntityTrait> From<ActixAdminModelFilter<T>> for ActixAdminViewModelFilter {
|
||||
fn from(filter: ActixAdminModelFilter<T>) -> Self {
|
||||
ActixAdminViewModelFilter {
|
||||
name: filter.name,
|
||||
value: None
|
||||
value: None,
|
||||
values: None,
|
||||
filter_type: Some(filter.filter_type)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -90,6 +90,8 @@ pub async fn list<E: ActixAdminViewModelTrait>(
|
||||
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()),
|
||||
values: None,
|
||||
filter_type: None
|
||||
};
|
||||
af
|
||||
}).collect();
|
||||
@ -143,7 +145,7 @@ pub async fn list<E: ActixAdminViewModelTrait>(
|
||||
ctx.insert("notifications", ¬ifications);
|
||||
ctx.insert("entities_per_page", &entities_per_page);
|
||||
ctx.insert("render_partial", &render_partial);
|
||||
ctx.insert("viewmodel_filter", &E::get_viewmodel_filter().await);
|
||||
ctx.insert("viewmodel_filter", &E::get_viewmodel_filter(&db).await);
|
||||
ctx.insert(
|
||||
"view_model",
|
||||
&ActixAdminViewModelSerializable::from(view_model.clone()),
|
||||
|
@ -7,11 +7,42 @@
|
||||
<ul class="menu-list">
|
||||
{% for key, value in viewmodel_filter %}
|
||||
<li>
|
||||
<div class="field">
|
||||
<div class="field mt-3">
|
||||
<label class="label">{{key}}</label>
|
||||
{% if value.filter_type == "Text" %}
|
||||
<div class="control">
|
||||
<input class="input" type="text" placeholder="" name="filter_{{key}}">
|
||||
<input class="input" value="{{ value.value }}" type="text" placeholder="" name="filter_{{key}}">
|
||||
</div>
|
||||
{% elif value.filter_type == "DateTime" %}
|
||||
<div class="control">
|
||||
<input class="input" value="{{ value.value }}" type="datetime-local" placeholder="" name="filter_{{key}}">
|
||||
</div>
|
||||
{% elif value.filter_type == "Checkbox" %}
|
||||
<div class="select is-fullwidth">
|
||||
<select name="filter_{{key}}" id="filter_{{key}}">
|
||||
<option value=""></option>
|
||||
<option value="1">✓</option>
|
||||
<option value="0">❌</option>
|
||||
</select>
|
||||
</div>
|
||||
{% elif value.filter_type == "Date" %}
|
||||
<div class="control">
|
||||
<input class="input" type="date" placeholder="" name="filter_{{key}}">
|
||||
</div>
|
||||
{% elif value.filter_type == "SelectList" %}
|
||||
<div class="select is-fullwidth">
|
||||
<select name="filter_{{key}}" id="filter_{{key}}">
|
||||
<option value=""></option>
|
||||
{% for selectval in value.values %}
|
||||
<option value="{{ selectval[0] }}">{{ selectval[1] }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</div>
|
||||
{% else %}
|
||||
<div class="control">
|
||||
<input class="input" value="{{ value.value }}" type="text" placeholder="" name="filter_{{key}}">
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</li>
|
||||
{% endfor %}
|
||||
@ -59,7 +90,8 @@
|
||||
</div>
|
||||
</div>
|
||||
<form id="search_form" action="/admin/{{ entity_name }}/list" hx-boost="true" hx-indicator="#loading"
|
||||
hx-target="#{{ entity_name }}table" hx-trigger="reload_table from:#entities_per_page" hx-include="[id='filter_form']">
|
||||
hx-target="#{{ entity_name }}table" hx-trigger="reload_table from:#entities_per_page"
|
||||
hx-include="[id='filter_form']">
|
||||
<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" name="page" value="{{ page }}">
|
||||
|
@ -3,7 +3,7 @@ use regex::Regex;
|
||||
use sea_orm::DatabaseConnection;
|
||||
use serde_derive::{Serialize, Deserialize};
|
||||
use std::collections::HashMap;
|
||||
use crate::{ActixAdminModel, SortOrder};
|
||||
use crate::{ActixAdminModel, SortOrder, model::ActixAdminModelFilterType};
|
||||
use actix_session::{Session};
|
||||
use std::convert::From;
|
||||
use crate::ActixAdminError;
|
||||
@ -26,7 +26,7 @@ pub trait ActixAdminViewModelTrait {
|
||||
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 get_select_lists(db: &DatabaseConnection) -> Result<HashMap<String, Vec<(String, String)>>, ActixAdminError>;
|
||||
async fn get_viewmodel_filter() -> HashMap<String, ActixAdminViewModelFilter>;
|
||||
async fn get_viewmodel_filter(db: &DatabaseConnection) -> HashMap<String, ActixAdminViewModelFilter>;
|
||||
fn validate_entity(model: &mut ActixAdminModel);
|
||||
|
||||
fn get_entity_name() -> String;
|
||||
@ -58,7 +58,9 @@ pub struct ActixAdminViewModelSerializable {
|
||||
#[derive(Clone, Debug, Serialize)]
|
||||
pub struct ActixAdminViewModelFilter {
|
||||
pub name: String,
|
||||
pub value: Option<String>
|
||||
pub value: Option<String>,
|
||||
pub values: Option<Vec<(String, String)>>,
|
||||
pub filter_type: Option<ActixAdminModelFilterType>
|
||||
}
|
||||
|
||||
// TODO: better alternative to serialize only specific fields for ActixAdminViewModel
|
||||
|
Loading…
Reference in New Issue
Block a user