From cf9a7cf33fedd43d955001777acbcf3e05245561 Mon Sep 17 00:00:00 2001 From: gabatxo1312 Date: Thu, 29 Jan 2026 23:52:55 +0100 Subject: [PATCH] Add edit and new for users --- src/lib.rs | 5 ++- src/models/book.rs | 11 ++++- src/models/user.rs | 16 ++++++++ src/routes/user.rs | 60 ++++++++++++++++++++++++++-- templates/books/new.html | 47 ++++++---------------- templates/components/dropdown.html | 8 ++-- templates/components/inputs.html | 43 ++++++++++++++++++++ templates/components/typography.html | 2 +- templates/index.html | 50 +++++++++++++---------- templates/users/edit.html | 22 ++++++++++ templates/users/index.html | 32 ++++++--------- templates/users/new.html | 21 ++++++++++ 12 files changed, 229 insertions(+), 88 deletions(-) create mode 100644 templates/components/inputs.html create mode 100644 templates/users/edit.html create mode 100644 templates/users/new.html diff --git a/src/lib.rs b/src/lib.rs index d9a0fa0..dd7479d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -23,8 +23,11 @@ pub fn build_app(state: AppState) -> Router { .route("/books/{id}/edit", get(routes::book::edit)) .route("/books/new", get(routes::book::new)) .route("/users", get(routes::user::index)) + .route("/users/new", get(routes::user::new)) + .route("/users/{id}/edit", get(routes::user::edit)) + .route("/users/{id}", post(routes::user::update)) .route("/users", post(routes::user::create)) - .route("/users/{id}", post(routes::user::delete)) + .route("/users/{id}/delete", post(routes::user::delete)) .nest("/assets", static_router()) .with_state(state) } diff --git a/src/models/book.rs b/src/models/book.rs index ea84252..e2a33e7 100644 --- a/src/models/book.rs +++ b/src/models/book.rs @@ -108,7 +108,7 @@ impl BookOperator { let book_pages = Entity::find() .filter(conditions) .order_by_desc(Column::Id) - .paginate(&self.state.db, 1); + .paginate(&self.state.db, 100); let books = book_pages .fetch_page(page_0indexed) @@ -140,6 +140,15 @@ impl BookOperator { } } + /// Finds vec of book by its Owner + pub async fn find_all_by_owner(&self, owner_id: i32) -> Result, BookError> { + Entity::find() + .filter(Column::OwnerId.eq(owner_id)) + .all(&self.state.db) + .await + .context(DBSnafu) + } + /// Creates a new book from the given form data. pub async fn create(&self, form: BookForm) -> Result { let book = ActiveModel { diff --git a/src/models/user.rs b/src/models/user.rs index 0311fa4..0699e42 100644 --- a/src/models/user.rs +++ b/src/models/user.rs @@ -1,5 +1,7 @@ +use crate::models::book::BookOperator; use crate::routes::user::UserForm; use crate::state::AppState; +use crate::state::error::UserSnafu; use sea_orm::ActiveValue::Set; use sea_orm::DeleteResult; use sea_orm::entity::prelude::*; @@ -74,6 +76,20 @@ impl UserOperator { user.insert(&self.state.db).await.context(DBSnafu) } + pub async fn update(&self, id: i32, form: UserForm) -> Result { + let user_by_id = Self::find_by_id(&self, id).await.context(UserSnafu); + + if let Ok(user) = user_by_id { + let mut user: ActiveModel = user.into(); + + user.name = Set(form.name); + + user.update(&self.state.db).await.context(DBSnafu) + } else { + Err(UserError::NotFound { id }) + } + } + pub async fn delete(&self, user_id: i32) -> Result { let user: Option = Entity::find_by_id(user_id) .one(&self.state.db) diff --git a/src/routes/user.rs b/src/routes/user.rs index 8a49cb4..b245d42 100644 --- a/src/routes/user.rs +++ b/src/routes/user.rs @@ -24,7 +24,16 @@ use crate::{ #[derive(Template, WebTemplate)] #[template(path = "users/index.html")] struct UsersIndexTemplate { - user_with_books_number: Vec<(user::Model, usize, usize)>, + user_with_books_number: Vec, +} + +pub struct UserWithBookNumber { + /// the user model + pub user: user::Model, + /// the number of books owned by this user + pub owner_book_number: usize, + /// the number of books borrowed by this user + pub borrowed_book_number: usize, } pub async fn index( @@ -40,7 +49,7 @@ pub async fn index( .await .context(BookSnafu)?; - let mut result: Vec<(user::Model, usize, usize)> = vec![]; + let mut result: Vec = Vec::with_capacity(users.len()); let mut owner_books: HashMap = HashMap::new(); let mut borrowed_books: HashMap = HashMap::new(); @@ -56,7 +65,11 @@ pub async fn index( let owner_books_size = owner_books.get(&user.id).unwrap_or(&0); let borrowed_books_size = borrowed_books.get(&user.id).unwrap_or(&0); - result.push((user, *owner_books_size, *borrowed_books_size)); + result.push(UserWithBookNumber { + user, + owner_book_number: *owner_books_size, + borrowed_book_number: *borrowed_books_size, + }); } Ok(UsersIndexTemplate { @@ -73,7 +86,7 @@ pub async fn create( State(state): State, Form(form): Form, ) -> Result { - let _user = UserOperator::new(state) + let _ = UserOperator::new(state) .create(form) .await .context(UserSnafu)?; @@ -81,6 +94,19 @@ pub async fn create( Ok(Redirect::to("/users")) } +pub async fn update( + State(state): State, + Path(id): Path, + Form(form): Form, +) -> Result { + let _ = UserOperator::new(state) + .update(id, form) + .await + .context(UserSnafu)?; + + Ok(Redirect::to("/users")) +} + pub async fn delete( State(state): State, Path(id): Path, @@ -92,3 +118,29 @@ pub async fn delete( Ok(Redirect::to("/users")) } + +#[derive(Template, WebTemplate)] +#[template(path = "users/edit.html")] +struct EditTemplate { + user: user::Model, +} + +pub async fn edit( + State(state): State, + Path(id): Path, +) -> Result { + let user = UserOperator::new(state) + .find_by_id(id) + .await + .context(UserSnafu)?; + + Ok(EditTemplate { user }) +} + +#[derive(Template, WebTemplate)] +#[template(path = "users/new.html")] +struct NewTemplate {} + +pub async fn new() -> impl axum::response::IntoResponse { + NewTemplate {} +} diff --git a/templates/books/new.html b/templates/books/new.html index b37a020..ddd66ce 100644 --- a/templates/books/new.html +++ b/templates/books/new.html @@ -1,50 +1,27 @@ {% extends "base.html" %} {% import "components/typography.html" as typography %} {% import "components/cards.html" as cards %} +{% import "components/inputs.html" as form_helpers %} {% block main %} {{ typography::heading("New book") }} {% call cards::card() %}
-
- - -
+ {{ form_helpers::input("title", "Name", is_required = true, placeholder = "Ex: La Petite Dernière") }} + {{ form_helpers::input("authors", "Author(s)", is_required = true, placeholder = "Ex: Fatima Daas") }} -
- - -
+ {% call(option) form_helpers::select("owner_id", "Owner", users, is_required = true) %} + + {% endcall %} -
- - -
+ {% call(option) form_helpers::select("current_holder_id", "Current Holder", users, is_required = false) %} + + {% endcall %} + + {{ form_helpers::textarea("description", "Description", rows = 5, is_required = false, placeholder = "Ex: Je m’appelle Fatima Daas. Je suis la mazoziya, la petite dernière. Celle à laquelle on ne s’est pas préparé. Française d’origine algérienne.") }} -
- - -
- -
- - -
- -
- - -
+ {{ form_helpers::textarea("comment", "Comment", rows = 3, is_required = false, placeholder = "Ex: I recommend it, it's great!") }}
diff --git a/templates/components/dropdown.html b/templates/components/dropdown.html index a792d72..ecd95a7 100644 --- a/templates/components/dropdown.html +++ b/templates/components/dropdown.html @@ -11,18 +11,18 @@
{% endmacro %} -{% macro book_dropdown_button(book, show = true) %} +{% macro crud_dropdown_button(book, label, sub_path, show = true) %}
{% endmacro %} diff --git a/templates/index.html b/templates/index.html index a1882c9..183f730 100644 --- a/templates/index.html +++ b/templates/index.html @@ -33,7 +33,7 @@
- {% match query.owner_id %} {% when Some with (owner_id) %} @@ -55,7 +55,7 @@
- {% match query.current_holder_id %} {% when Some with (current_holder_id) %} @@ -75,7 +75,11 @@
- + +
+ +
+ Reset
@@ -95,7 +99,7 @@ {% for book_user in books_with_user %} - + {{ book_user.book.id }} {{ book_user.book.title }} {{ book_user.book.authors }} @@ -109,7 +113,7 @@ {% endmatch %} - {{ dropdown::book_dropdown_button(book_user.book) }} + {{ dropdown::crud_dropdown_button(book_user.book, "Actions", "books") }} {% endfor %} @@ -117,25 +121,27 @@ {% if total_page > 1 %} - + {% endif %} {% endcall %} {% endblock %} diff --git a/templates/users/edit.html b/templates/users/edit.html new file mode 100644 index 0000000..3b87fec --- /dev/null +++ b/templates/users/edit.html @@ -0,0 +1,22 @@ +{% extends "base.html" %} +{% import "components/typography.html" as typography %} +{% import "components/cards.html" as cards %} +o{% import "components/inputs.html" as form_helpers %} + +{% block main %} + {{ typography::heading("New User") }} + + {% call cards::card() %} +
+
+
+ {{ form_helpers::input("name", "Name", value = user.name, is_required = true, placeholder = "Ex: Kropotkine", margin_bottom = false) }} +
+ +
+ +
+
+
+ {% endcall %} +{% endblock %} diff --git a/templates/users/index.html b/templates/users/index.html index 6d424d2..459bdc1 100644 --- a/templates/users/index.html +++ b/templates/users/index.html @@ -1,9 +1,13 @@ {% extends "base.html" %} {% import "components/typography.html" as typography %} +{% import "components/dropdown.html" as dropdown %} {% import "components/cards.html" as cards %} +{% import "components/inputs.html" as form_helpers %} {% block main %} - {{ typography::heading("All users") }} + {% call typography::heading("All users") %} + Add User + {% endcall %} {% call cards::card() %} @@ -17,30 +21,18 @@ - {% for (user, book_size, borrowed_book) in user_with_books_number %} + {% for user_information in user_with_books_number %} - - - - + + + + {% endfor %} -
{{ user.id }}{{ user.name }}{{ book_size }}{{ borrowed_book }}{{ user_information.user.id }}{{ user_information.user.name }}{{ user_information.owner_book_number }}{{ user_information.borrowed_book_number }} -
- -
+ {{ dropdown::crud_dropdown_button(user_information.user, "Actions", "users", show = false) }}
+ {% endcall %} - - {% call cards::card() %} -
- - - - -
- {% endcall %} - {% endblock %} diff --git a/templates/users/new.html b/templates/users/new.html new file mode 100644 index 0000000..c3ebcd4 --- /dev/null +++ b/templates/users/new.html @@ -0,0 +1,21 @@ +{% extends "base.html" %} +{% import "components/typography.html" as typography %} +{% import "components/cards.html" as cards %} +o{% import "components/inputs.html" as form_helpers %} + +{% block main %} + {{ typography::heading("New User") }} + + {% call cards::card() %} +
+
+
+ {{ form_helpers::input("name", "Name", is_required = true, placeholder = "Ex: Kropotkine", margin_bottom = false) }} +
+
+ +
+
+
+ {% endcall %} +{% endblock %}