keep list params while in edit or show

This commit is contained in:
Manuel Gugger 2023-02-03 17:48:45 +01:00
parent 8ed3796e45
commit 683e473749
9 changed files with 262 additions and 96 deletions

View File

@ -6,11 +6,13 @@ use crate::ActixAdminNotification;
use crate::prelude::*; use crate::prelude::*;
use crate::TERA; use crate::TERA;
use super::DEFAULT_ENTITIES_PER_PAGE;
use super::Params;
use super::{ add_auth_context, user_can_access_page, render_unauthorized}; use super::{ add_auth_context, user_can_access_page, render_unauthorized};
pub async fn create_get<T: ActixAdminAppDataTrait, E: ActixAdminViewModelTrait>( pub async fn create_get<T: ActixAdminAppDataTrait, E: ActixAdminViewModelTrait>(
session: Session, session: Session,
_req: HttpRequest, req: HttpRequest,
data: web::Data<T>, data: web::Data<T>,
_body: web::Payload, _body: web::Payload,
_text: String, _text: String,
@ -18,12 +20,12 @@ pub async fn create_get<T: ActixAdminAppDataTrait, E: ActixAdminViewModelTrait>(
let db = &data.get_db(); let db = &data.get_db();
let model = ActixAdminModel::create_empty(); let model = ActixAdminModel::create_empty();
create_or_edit_get::<T, E>(&session, &data, db, Ok(model)).await create_or_edit_get::<T, E>(&session, req, &data, db, Ok(model)).await
} }
pub async fn edit_get<T: ActixAdminAppDataTrait, E: ActixAdminViewModelTrait>( pub async fn edit_get<T: ActixAdminAppDataTrait, E: ActixAdminViewModelTrait>(
session: Session, session: Session,
_req: HttpRequest, req: HttpRequest,
data: web::Data<T>, data: web::Data<T>,
_text: String, _text: String,
id: web::Path<i32> id: web::Path<i32>
@ -31,10 +33,10 @@ pub async fn edit_get<T: ActixAdminAppDataTrait, E: ActixAdminViewModelTrait>(
let db = &data.get_db(); let db = &data.get_db();
let model = E::get_entity(db, id.into_inner()).await; let model = E::get_entity(db, id.into_inner()).await;
create_or_edit_get::<T, E>(&session, &data, db, model).await create_or_edit_get::<T, E>(&session, req, &data, db, model).await
} }
async fn create_or_edit_get<T: ActixAdminAppDataTrait, E: ActixAdminViewModelTrait>(session: &Session, data: &web::Data<T>, db: &sea_orm::DatabaseConnection, model_result: Result<ActixAdminModel, ActixAdminError>) -> Result<HttpResponse, Error>{ async fn create_or_edit_get<T: ActixAdminAppDataTrait, E: ActixAdminViewModelTrait>(session: &Session, req: HttpRequest, data: &web::Data<T>, db: &sea_orm::DatabaseConnection, model_result: Result<ActixAdminModel, ActixAdminError>) -> Result<HttpResponse, Error>{
let actix_admin = &data.get_actix_admin(); let actix_admin = &data.get_actix_admin();
let mut ctx = Context::new(); let mut ctx = Context::new();
add_auth_context(&session, actix_admin, &mut ctx); add_auth_context(&session, actix_admin, &mut ctx);
@ -68,11 +70,28 @@ async fn create_or_edit_get<T: ActixAdminAppDataTrait, E: ActixAdminViewModelTra
.map(|err| ActixAdminNotification::from(err)) .map(|err| ActixAdminNotification::from(err))
.collect(); .collect();
let params = web::Query::<Params>::from_query(req.query_string()).unwrap();
let page = params.page.unwrap_or(1);
let entities_per_page = params
.entities_per_page
.unwrap_or(DEFAULT_ENTITIES_PER_PAGE);
let render_partial = params.render_partial.unwrap_or(false);
let search = params.search.clone().unwrap_or(String::new());
let sort_by = params.sort_by.clone().unwrap_or(view_model.primary_key.to_string());
let sort_order = params.sort_order.as_ref().unwrap_or(&SortOrder::Asc);
ctx.insert("view_model", &ActixAdminViewModelSerializable::from(view_model.clone())); ctx.insert("view_model", &ActixAdminViewModelSerializable::from(view_model.clone()));
ctx.insert("select_lists", &E::get_select_lists(db).await?); ctx.insert("select_lists", &E::get_select_lists(db).await?);
ctx.insert("base_path", &E::get_base_path(&entity_name)); ctx.insert("base_path", &E::get_base_path(&entity_name));
ctx.insert("model", &model); ctx.insert("model", &model);
ctx.insert("notifications", &notifications); ctx.insert("notifications", &notifications);
ctx.insert("entities_per_page", &entities_per_page);
ctx.insert("render_partial", &render_partial);
ctx.insert("search", &search);
ctx.insert("sort_by", &sort_by);
ctx.insert("sort_order", &sort_order);
ctx.insert("page", &page);
let body = TERA let body = TERA
.render("create_or_edit.html", &ctx) .render("create_or_edit.html", &ctx)

View File

@ -1,4 +1,5 @@
use super::{render_unauthorized, user_can_access_page}; use super::{render_unauthorized, user_can_access_page};
use super::{Params, DEFAULT_ENTITIES_PER_PAGE};
use crate::prelude::*; use crate::prelude::*;
use crate::ActixAdminError; use crate::ActixAdminError;
use crate::ActixAdminNotification; use crate::ActixAdminNotification;
@ -7,26 +8,32 @@ use actix_multipart::Multipart;
use actix_multipart::MultipartError; use actix_multipart::MultipartError;
use actix_session::Session; use actix_session::Session;
use actix_web::http::header; use actix_web::http::header;
use actix_web::{error, web, Error, HttpResponse}; use actix_web::{error, web, Error, HttpRequest, HttpResponse};
use std::collections::HashMap; use std::collections::HashMap;
use tera::Context; use tera::Context;
pub async fn create_post<T: ActixAdminAppDataTrait, E: ActixAdminViewModelTrait>( pub async fn create_post<T: ActixAdminAppDataTrait, E: ActixAdminViewModelTrait>(
session: Session, session: Session,
req: HttpRequest,
data: web::Data<T>, data: web::Data<T>,
payload: Multipart, payload: Multipart,
) -> Result<HttpResponse, Error> { ) -> Result<HttpResponse, Error> {
let actix_admin = data.get_actix_admin(); let actix_admin = data.get_actix_admin();
let model = ActixAdminModel::create_from_payload( let model = ActixAdminModel::create_from_payload(
payload, payload,
&format!("{}/{}", actix_admin.configuration.file_upload_directory, E::get_entity_name()) &format!(
"{}/{}",
actix_admin.configuration.file_upload_directory,
E::get_entity_name()
),
) )
.await; .await;
create_or_edit_post::<T, E>(&session, &data, model, None, actix_admin).await create_or_edit_post::<T, E>(&session, req, &data, model, None, actix_admin).await
} }
pub async fn edit_post<T: ActixAdminAppDataTrait, E: ActixAdminViewModelTrait>( pub async fn edit_post<T: ActixAdminAppDataTrait, E: ActixAdminViewModelTrait>(
session: Session, session: Session,
req: HttpRequest,
data: web::Data<T>, data: web::Data<T>,
payload: Multipart, payload: Multipart,
id: web::Path<i32>, id: web::Path<i32>,
@ -34,14 +41,27 @@ pub async fn edit_post<T: ActixAdminAppDataTrait, E: ActixAdminViewModelTrait>(
let actix_admin = data.get_actix_admin(); let actix_admin = data.get_actix_admin();
let model = ActixAdminModel::create_from_payload( let model = ActixAdminModel::create_from_payload(
payload, payload,
&format!("{}/{}", actix_admin.configuration.file_upload_directory, E::get_entity_name()), &format!(
"{}/{}",
actix_admin.configuration.file_upload_directory,
E::get_entity_name()
),
) )
.await; .await;
create_or_edit_post::<T, E>(&session, &data, model, Some(id.into_inner()), actix_admin).await create_or_edit_post::<T, E>(
&session,
req,
&data,
model,
Some(id.into_inner()),
actix_admin,
)
.await
} }
pub async fn create_or_edit_post<T: ActixAdminAppDataTrait, E: ActixAdminViewModelTrait>( pub async fn create_or_edit_post<T: ActixAdminAppDataTrait, E: ActixAdminViewModelTrait>(
session: &Session, session: &Session,
req: HttpRequest,
data: &web::Data<T>, data: &web::Data<T>,
model_res: Result<ActixAdminModel, MultipartError>, model_res: Result<ActixAdminModel, MultipartError>,
id: Option<i32>, id: Option<i32>,
@ -64,7 +84,16 @@ pub async fn create_or_edit_post<T: ActixAdminAppDataTrait, E: ActixAdminViewMod
if model.has_errors() { if model.has_errors() {
errors.push(ActixAdminError::ValidationErrors); errors.push(ActixAdminError::ValidationErrors);
render_form::<E>(actix_admin, view_model, db, entity_name, &model, errors).await render_form::<E>(
req,
actix_admin,
view_model,
db,
entity_name,
&model,
errors,
)
.await
} else { } else {
let res = match id { let res = match id {
Some(id) => E::edit_entity(db, id, model.clone()).await, Some(id) => E::edit_entity(db, id, model.clone()).await,
@ -72,21 +101,47 @@ pub async fn create_or_edit_post<T: ActixAdminAppDataTrait, E: ActixAdminViewMod
}; };
match res { match res {
Ok(_) => Ok(HttpResponse::SeeOther() Ok(_) => {
let params = web::Query::<Params>::from_query(req.query_string()).unwrap();
let page = params.page.unwrap_or(1);
let entities_per_page = params
.entities_per_page
.unwrap_or(DEFAULT_ENTITIES_PER_PAGE);
let _render_partial = params.render_partial.unwrap_or(false);
let search = params.search.clone().unwrap_or(String::new());
let sort_by = params
.sort_by
.clone()
.unwrap_or(view_model.primary_key.to_string());
let sort_order = params.sort_order.as_ref().unwrap_or(&SortOrder::Asc);
Ok(HttpResponse::SeeOther()
.append_header(( .append_header((
header::LOCATION, header::LOCATION,
format!("/admin/{}/list", view_model.entity_name), format!("/admin/{0}/list?page={1}&search={2}&sort_by={3}&sort_order={4}&entities_per_page={5}", view_model.entity_name, page, search, sort_by, sort_order, entities_per_page),
)) ))
.finish()), .finish())
}
Err(e) => { Err(e) => {
errors.push(e); errors.push(e);
render_form::<E>(actix_admin, view_model, db, entity_name, &model, errors).await render_form::<E>(
req,
actix_admin,
view_model,
db,
entity_name,
&model,
errors,
)
.await
} }
} }
} }
} }
async fn render_form<E: ActixAdminViewModelTrait>( async fn render_form<E: ActixAdminViewModelTrait>(
req: HttpRequest,
actix_admin: &ActixAdmin, actix_admin: &ActixAdmin,
view_model: &ActixAdminViewModel, view_model: &ActixAdminViewModel,
db: &&sea_orm::DatabaseConnection, db: &&sea_orm::DatabaseConnection,
@ -95,6 +150,28 @@ async fn render_form<E: ActixAdminViewModelTrait>(
errors: Vec<ActixAdminError>, errors: Vec<ActixAdminError>,
) -> Result<HttpResponse, Error> { ) -> Result<HttpResponse, Error> {
let mut ctx = Context::new(); let mut ctx = Context::new();
let params = web::Query::<Params>::from_query(req.query_string()).unwrap();
let page = params.page.unwrap_or(1);
let entities_per_page = params
.entities_per_page
.unwrap_or(DEFAULT_ENTITIES_PER_PAGE);
let render_partial = params.render_partial.unwrap_or(false);
let search = params.search.clone().unwrap_or(String::new());
let sort_by = params
.sort_by
.clone()
.unwrap_or(view_model.primary_key.to_string());
let sort_order = params.sort_order.as_ref().unwrap_or(&SortOrder::Asc);
ctx.insert("entities_per_page", &entities_per_page);
ctx.insert("render_partial", &render_partial);
ctx.insert("search", &search);
ctx.insert("sort_by", &sort_by);
ctx.insert("sort_order", &sort_order);
ctx.insert("page", &page);
ctx.insert("entity_names", &actix_admin.entity_names); ctx.insert("entity_names", &actix_admin.entity_names);
ctx.insert( ctx.insert(
"view_model", "view_model",

View File

@ -1,3 +1,5 @@
use std::fmt;
use actix_web::{error, web, Error, HttpRequest, HttpResponse}; use actix_web::{error, web, Error, HttpRequest, HttpResponse};
use serde::Serialize; use serde::Serialize;
use serde::{Deserialize}; use serde::{Deserialize};
@ -10,19 +12,7 @@ use crate::ActixAdminModel;
use crate::ActixAdminNotification; use crate::ActixAdminNotification;
use crate::TERA; use crate::TERA;
use actix_session::{Session}; use actix_session::{Session};
use super::{ add_auth_context, user_can_access_page, render_unauthorized}; use super::{ add_auth_context, user_can_access_page, render_unauthorized, Params, DEFAULT_ENTITIES_PER_PAGE};
const DEFAULT_ENTITIES_PER_PAGE: u64 = 10;
#[derive(Debug, Deserialize)]
pub struct Params {
page: Option<u64>,
entities_per_page: Option<u64>,
render_partial: Option<bool>,
search: Option<String>,
sort_by: Option<String>,
sort_order: Option<SortOrder>
}
#[derive(Debug, Serialize, Deserialize, PartialEq, Eq)] #[derive(Debug, Serialize, Deserialize, PartialEq, Eq)]
pub enum SortOrder { pub enum SortOrder {
@ -30,6 +20,15 @@ pub enum SortOrder {
Desc, Desc,
} }
impl fmt::Display for SortOrder {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
SortOrder::Asc => write!(f, "Asc"),
SortOrder::Desc => write!(f, "Desc"),
}
}
}
pub async fn list<T: ActixAdminAppDataTrait, E: ActixAdminViewModelTrait>( pub async fn list<T: ActixAdminAppDataTrait, E: ActixAdminViewModelTrait>(
session: Session, session: Session,
req: HttpRequest, req: HttpRequest,

View File

@ -20,4 +20,17 @@ mod helpers;
pub use helpers::{ add_auth_context, user_can_access_page, render_unauthorized }; pub use helpers::{ add_auth_context, user_can_access_page, render_unauthorized };
mod file; mod file;
pub use file::{download, delete_file}; pub use file::{download, delete_file};
use serde::{Deserialize};
#[derive(Debug, Deserialize)]
pub struct Params {
page: Option<u64>,
entities_per_page: Option<u64>,
render_partial: Option<bool>,
search: Option<String>,
sort_by: Option<String>,
sort_order: Option<SortOrder>
}
const DEFAULT_ENTITIES_PER_PAGE: u64 = 10;

View File

@ -1,3 +1,4 @@
use actix_web::HttpRequest;
use actix_web::{error, web, Error, HttpResponse}; use actix_web::{error, web, Error, HttpResponse};
use actix_session::{Session}; use actix_session::{Session};
use tera::{Context}; use tera::{Context};
@ -6,9 +7,10 @@ use crate::ActixAdminNotification;
use crate::prelude::*; use crate::prelude::*;
use crate::TERA; use crate::TERA;
use super::{Params, DEFAULT_ENTITIES_PER_PAGE};
use super::{ add_auth_context, user_can_access_page, render_unauthorized}; use super::{ add_auth_context, user_can_access_page, render_unauthorized};
pub async fn show<T: ActixAdminAppDataTrait, E: ActixAdminViewModelTrait>(session: Session, data: web::Data<T>, id: web::Path<i32>) -> Result<HttpResponse, Error> { pub async fn show<T: ActixAdminAppDataTrait, E: ActixAdminViewModelTrait>(session: Session, req: HttpRequest, data: web::Data<T>, id: web::Path<i32>) -> Result<HttpResponse, Error> {
let actix_admin = data.get_actix_admin(); let actix_admin = data.get_actix_admin();
let db = &data.get_db(); let db = &data.get_db();
@ -40,11 +42,28 @@ pub async fn show<T: ActixAdminAppDataTrait, E: ActixAdminViewModelTrait>(sessio
.map(|err| ActixAdminNotification::from(err)) .map(|err| ActixAdminNotification::from(err))
.collect(); .collect();
let params = web::Query::<Params>::from_query(req.query_string()).unwrap();
let page = params.page.unwrap_or(1);
let entities_per_page = params
.entities_per_page
.unwrap_or(DEFAULT_ENTITIES_PER_PAGE);
let render_partial = params.render_partial.unwrap_or(false);
let search = params.search.clone().unwrap_or(String::new());
let sort_by = params.sort_by.clone().unwrap_or(view_model.primary_key.to_string());
let sort_order = params.sort_order.as_ref().unwrap_or(&SortOrder::Asc);
ctx.insert("model", &model); ctx.insert("model", &model);
ctx.insert("view_model", &ActixAdminViewModelSerializable::from(view_model.clone())); ctx.insert("view_model", &ActixAdminViewModelSerializable::from(view_model.clone()));
ctx.insert("base_path", &E::get_base_path(&entity_name)); ctx.insert("base_path", &E::get_base_path(&entity_name));
ctx.insert("entity_names", &actix_admin.entity_names); ctx.insert("entity_names", &actix_admin.entity_names);
ctx.insert("notifications", &notifications); ctx.insert("notifications", &notifications);
ctx.insert("entities_per_page", &entities_per_page);
ctx.insert("render_partial", &render_partial);
ctx.insert("search", &search);
ctx.insert("sort_by", &sort_by);
ctx.insert("sort_order", &sort_order);
ctx.insert("page", &page);
add_auth_context(&session, actix_admin, &mut ctx); add_auth_context(&session, actix_admin, &mut ctx);

View File

@ -2,6 +2,11 @@
{% block content %} {% block content %}
<form hx-boost="true" hx-indicator="#loading" hx-push-url="true" hx-encoding="multipart/form-data" method="post" enctype="multipart/form-data"> <form hx-boost="true" hx-indicator="#loading" hx-push-url="true" hx-encoding="multipart/form-data" method="post" enctype="multipart/form-data">
<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="entities_per_page" value="{{ entities_per_page }}">
<input type="hidden" name="search" value="{{ search }}">
<input type="hidden" name="page" value="{{ page }}">
{% for model_field in view_model.fields -%} {% for model_field in view_model.fields -%}
<div class="field"> <div class="field">
<label class="{{ model_field | get_html_input_type }}" for="{{ model_field.field_name }}"> <label class="{{ model_field | get_html_input_type }}" for="{{ model_field.field_name }}">
@ -29,7 +34,17 @@
<button class="button is-link" type="submit">Save</i></button> <button class="button is-link" type="submit">Save</i></button>
</div> </div>
<div class="control"> <div class="control">
<a class="button is-link is-light" href="{{ base_path }}/list">Cancel</a> <a hx-vals='{
"entities_per_page" : "{{ entities_per_page }}",
"search" : "{{ search }}",
"sort_by" : "{{ sort_by }}",
"sort_order" : "{{ sort_order }}",
"render_partial" : "false",
"page" : "{{ page }}"
}' hx-boost="true" hx-push-url="true" hx-indicator="#loading"
class="button is-link is-light" href="{{ base_path }}/list">
Cancel
</a>
</div> </div>
</div> </div>
</form> </form>

View File

@ -66,14 +66,14 @@
<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-vals='{ "render_partial" : "true" }' hx-target="#{{ entity_name }}table"> hx-vals='{ "render_partial" : "true" }' hx-target="#{{ entity_name }}table">
<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-fullwidth is-hoverable is-striped"> <table class="table is-relative is-fullwidth is-hoverable is-striped">
<thead> <thead>
<tr> <tr>
<th> <th>
<input type="checkbox" onclick="checkAll(this)"> <input type="checkbox" onclick="checkAll(this)">
@ -88,7 +88,8 @@
{% endif %} {% endif %}
{% endif %} {% endif %}
</th> </th>
{% for model_field in view_model.fields | filter(attribute="list_hide_column", value=false) | sort(attribute="list_sort_position") -%} {% for model_field in view_model.fields | filter(attribute="list_hide_column", value=false) |
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 %}
@ -105,70 +106,82 @@
<!-- 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="show/{{ entity.primary_key }}"> <a href="show/{{ entity.primary_key }}" hx-vals='{
<i class="fa-solid fa-magnifying-glass"></i> {{ entity.primary_key }} "page" : "{{ page }}",
</a> "entities_per_page" : "{{ entities_per_page }}",
</td> "search" : "{{ search }}",
{% for model_field in view_model.fields | filter(attribute="list_hide_column", value=false) | sort(attribute="list_sort_position") -%} "sort_by" : "{{ sort_by }}",
{% if model_field.field_type == "Checkbox" %} "sort_order" : "{{ sort_order }}",
<td>{{ entity.values | get(key=model_field.field_name) | get_icon | safe }}</td> "render_partial" : "true"
{% elif model_field.field_type == "FileUpload" %} }' hx-target="#content">
<td><a href="file/{{ entity.primary_key }}/{{ model_field.field_name }}">{{ <i class="fa-solid fa-magnifying-glass"></i> {{ entity.primary_key }}
entity.values </a>
| get(key=model_field.field_name) }}</a></td> </td>
{% else %} {% for model_field in view_model.fields | filter(attribute="list_hide_column", value=false) |
<td>{{ entity.values | get(key=model_field.field_name) }}</td> sort(attribute="list_sort_position") -%}
{% endif %} {% if model_field.field_type == "Checkbox" %}
{%- endfor %} <td>{{ entity.values | get(key=model_field.field_name) | get_icon | safe }}</td>
<td> {% elif model_field.field_type == "FileUpload" %}
<a hx-target="body" href="edit/{{ entity.primary_key }}"><i class="fa-solid fa-pen-to-square"></i></a> <td><a href="file/{{ entity.primary_key }}/{{ model_field.field_name }}">{{
<a hx-target="closest tr" hx-confirm="Are you sure?" entity.values
hx-delete="delete/{{ entity.primary_key }}"><i class="fa-solid fa-trash"></i></a> | get(key=model_field.field_name) }}</a></td>
</td> {% else %}
</tr> <td>{{ entity.values | get(key=model_field.field_name) }}</td>
{%- endfor %} {% endif %}
</tbody> {%- endfor %}
<tfoot> <td>
<tr> <a hx-target="body" href="edit/{{ entity.primary_key }}" hx-vals='{
<td colspan="{{ view_model.fields | length + 3 }}"> "page" : "{{ page }}",
</td> "entities_per_page" : "{{ entities_per_page }}",
</tr> "search" : "{{ search }}",
</tfoot> "sort_by" : "{{ sort_by }}",
"sort_order" : "{{ sort_order }}",
"render_partial" : "false"
}'>
<i class="fa-solid fa-pen-to-square"></i>
</a>
<a hx-target="closest tr" hx-confirm="Are you sure?" 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> </table>
</form> </form>
</div> </div>
<nav hx-boost="true" <nav hx-boost="true" hx-push-url="true" hx-target="#{{ entity_name }}table" hx-vals='{
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" }' hx-indicator="#loading" class="pagination is-rounded is-centered" role="pagination" aria-label="pagination">
aria-label="pagination">
{% if page > 1 %} {% if page > 1 %}
<a href="?&page={{ page - 1 }}" <a href="?&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> </a>
{% endif %} {% endif %}
{% if page < num_pages %} <a {% if page < num_pages %} <a href="?page={{ page + 1 }}" class="pagination-next right-arrow-click"><i
href="?page={{ page + 1 }}" class="fa-solid fa-arrow-right"></i>
class="pagination-next right-arrow-click"><i class="fa-solid fa-arrow-right"></i>
</a> </a>
{% endif %} {% endif %}
<ul class="pagination-list"> <ul class="pagination-list">
<li> <li>
<a class="pagination-link {% if page == 1 %}is-current{% endif %}" <a class="pagination-link {% if page == 1 %}is-current{% endif %}" href="?page={{ 1 }}"
href="?page={{ 1 }}"
aria-label="Goto page 1">1</a> aria-label="Goto page 1">1</a>
</li> </li>
<li> <li>
@ -176,8 +189,7 @@
</li> </li>
{% for i in range(start=min_show_page,end=max_show_page) %} {% for i in range(start=min_show_page,end=max_show_page) %}
<li><a class="pagination-link {% if page == i+1 %}is-current{% endif %}" <li><a class="pagination-link {% if page == i+1 %}is-current{% endif %}"
aria-label="Goto page {{ i + 1 }}" aria-label="Goto page {{ i + 1 }}" href="?page={{ i + 1 }}">{{
href="?page={{ i + 1 }}">{{
i + 1 }}</a></li> i + 1 }}</a></li>
{%- endfor %} {%- endfor %}
<li> <li>

View File

@ -9,7 +9,8 @@
{% if model_field.field_type == "Checkbox" %} {% if model_field.field_type == "Checkbox" %}
<td>{{ model.values | get(key=model_field.field_name) | get_icon | safe }}</td> <td>{{ model.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/{{ view_model.primary_key }}/{{ model_field.field_name }}">{{ model.values | get(key=model_field.field_name) }}</a></td> <td><a href="file/{{ view_model.primary_key }}/{{ model_field.field_name }}">{{ model.values |
get(key=model_field.field_name) }}</a></td>
{% else %} {% else %}
<td>{{ model.values | get(key=model_field.field_name) }}</td> <td>{{ model.values | get(key=model_field.field_name) }}</td>
{% endif %} {% endif %}
@ -21,11 +22,18 @@
<div class="column"> <div class="column">
<div class="field is-grouped"> <div class="field is-grouped">
<div class="control"> <div class="control">
<a class="button is-link is-light" href="{{ base_path }}/list">Back</a> <a hx-vals='{
"entities_per_page" : "{{ entities_per_page }}",
"search" : "{{ search }}",
"sort_by" : "{{ sort_by }}",
"sort_order" : "{{ sort_order }}",
"render_partial" : "false",
"page" : "{{ page }}"
}' hx-boost="true" hx-push-url="true" hx-indicator="#loading"
class="button is-link is-light" href="{{ base_path }}/list">Back</a>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
{% endblock content %} {% endblock content %}

View File

@ -1,5 +1,6 @@
use actix_admin::prelude::*; use actix_admin::prelude::*;
use actix_session::Session; use actix_session::Session;
use actix_web::HttpRequest;
use actix_web::web; use actix_web::web;
use actix_web::Error; use actix_web::Error;
use actix_web::HttpResponse; use actix_web::HttpResponse;
@ -135,16 +136,18 @@ pub fn create_actix_admin_builder() -> ActixAdminBuilder {
async fn create_post_from_plaintext<T: ActixAdminAppDataTrait, E: ActixAdminViewModelTrait>( async fn create_post_from_plaintext<T: ActixAdminAppDataTrait, E: ActixAdminViewModelTrait>(
session: Session, session: Session,
req: HttpRequest,
data: web::Data<T>, data: web::Data<T>,
text: String, text: String,
) -> Result<HttpResponse, Error> { ) -> Result<HttpResponse, Error> {
let actix_admin = data.get_actix_admin(); let actix_admin = data.get_actix_admin();
let model = ActixAdminModel::from(text); let model = ActixAdminModel::from(text);
create_or_edit_post::<T, E>(&session, &data, Ok(model), None, actix_admin).await create_or_edit_post::<T, E>(&session, req, &data, Ok(model), None, actix_admin).await
} }
async fn edit_post_from_plaintext<T: ActixAdminAppDataTrait, E: ActixAdminViewModelTrait>( async fn edit_post_from_plaintext<T: ActixAdminAppDataTrait, E: ActixAdminViewModelTrait>(
session: Session, session: Session,
req: HttpRequest,
data: web::Data<T>, data: web::Data<T>,
text: String, text: String,
id: web::Path<i32>, id: web::Path<i32>,
@ -153,6 +156,7 @@ async fn edit_post_from_plaintext<T: ActixAdminAppDataTrait, E: ActixAdminViewMo
let model = ActixAdminModel::from(text); let model = ActixAdminModel::from(text);
create_or_edit_post::<T, E>( create_or_edit_post::<T, E>(
&session, &session,
req,
&data, &data,
Ok(model), Ok(model),
Some(id.into_inner()), Some(id.into_inner()),