diff --git a/src/app/catalog.rs b/src/app/catalog.rs
new file mode 100644
index 0000000..e3864de
--- /dev/null
+++ b/src/app/catalog.rs
@@ -0,0 +1,54 @@
+use askama::Template;
+use serde::Deserialize;
+use axum_htmx::HxRequest;
+use axum::extract::State;
+use sqlx::SqlitePool;
+use askama_axum::{IntoResponse, Response};
+use crate::db::inventory_item::{inventory_item_get_all, inventory_item_get_search, DbInventoryItem};
+use crate::error::{AppError, QueryExtractor};
+
+#[derive(Template)]
+#[template(path = "catalog.html")]
+struct CatalogTemplate {
+ items: Vec,
+ query: CatalogQueryArgs,
+}
+
+#[derive(Template)]
+#[template(path = "catalog_item_fragment.html")]
+struct CatalogItemFragmentTemplate {
+ items: Vec
+}
+
+/// Query string response for "authorized" endpoint
+#[derive(Debug, Deserialize)]
+pub struct CatalogQueryArgs {
+ #[serde(rename = "q")]
+ pub search: Option,
+ #[serde(alias = "p")]
+ pub page: Option,
+ #[serde(rename = "size")]
+ pub page_size: Option,
+}
+
+#[axum::debug_handler]
+pub async fn catalog(
+ QueryExtractor(query): QueryExtractor,
+ HxRequest(hx_request): HxRequest,
+ State(db): State
+) -> Result {
+ let page = query.page.unwrap_or(0);
+ let page_size = query.page_size.unwrap_or(100);
+
+ let items = match query.search.as_ref() {
+ Some(s) => inventory_item_get_search(&db, &s, page_size, page).await?,
+ None => inventory_item_get_all(&db, page_size, page).await?,
+ };
+
+ if hx_request {
+ Ok(CatalogItemFragmentTemplate { items }.into_response())
+ }
+ else {
+ Ok(CatalogTemplate { items, query }.into_response())
+ }
+}
\ No newline at end of file
diff --git a/src/app/item.rs b/src/app/item.rs
new file mode 100644
index 0000000..dee7346
--- /dev/null
+++ b/src/app/item.rs
@@ -0,0 +1,26 @@
+use askama::Template;
+use askama_axum::IntoResponse;
+use axum::extract::{Path, State};
+use sqlx::SqlitePool;
+use axum::response::Response;
+use crate::db::inventory_item::{inventory_item_get_by_id, sum_all_adjustments_for_item, DbInventoryItem};
+use crate::error::AppError;
+
+#[derive(Template)]
+#[template(path = "item.html")]
+struct ItemTemplate {
+ item: DbInventoryItem
+}
+
+pub async fn item(State(db): State, Path(id): Path) -> Result {
+ let item = inventory_item_get_by_id(&db, id).await?;
+
+ Ok(ItemTemplate { item }.into_response())
+}
+
+
+pub async fn item_count(State(db): State, Path(id): Path) -> Result {
+ let count = sum_all_adjustments_for_item(&db, id).await?;
+
+ Ok(count.to_string().into_response())
+}
diff --git a/src/app/items.rs b/src/app/items.rs
deleted file mode 100644
index d559a7f..0000000
--- a/src/app/items.rs
+++ /dev/null
@@ -1,74 +0,0 @@
-use askama::Template;
-use askama_axum::IntoResponse;
-use axum::extract::{Path, State};
-use sqlx::SqlitePool;
-use axum::response::Response;
-use axum_htmx::HxRequest;
-use serde::Deserialize;
-use crate::db::inventory_item::{inventory_item_get_all, inventory_item_get_by_id, inventory_item_get_search, sum_all_adjustments_for_item, DbInventoryItem};
-use crate::error::{AppError, QueryExtractor};
-
-#[derive(Template)]
-#[template(path = "item_list.html")]
-struct ItemListTemplate {
- items: Vec,
- query: ItemsQueryArgs,
-}
-
-#[derive(Template)]
-#[template(path = "item_list_fragment.html")]
-struct ItemListFragmentTemplate {
- items: Vec
-}
-
-/// Query string response for "authorized" endpoint
-#[derive(Debug, Deserialize)]
-pub struct ItemsQueryArgs {
- #[serde(rename = "q")]
- pub search: Option,
- #[serde(alias = "p")]
- pub page: Option,
- #[serde(rename = "size")]
- pub page_size: Option,
-}
-
-#[axum::debug_handler]
-pub async fn item_list(
- QueryExtractor(query): QueryExtractor,
- HxRequest(hx_request): HxRequest,
- State(db): State
-) -> Result {
- let page = query.page.unwrap_or(0);
- let page_size = query.page_size.unwrap_or(100);
-
- let items = match query.search.as_ref() {
- Some(s) => inventory_item_get_search(&db, &s, page_size, page).await?,
- None => inventory_item_get_all(&db, page_size, page).await?,
- };
-
- if hx_request {
- Ok(ItemListFragmentTemplate { items }.into_response())
- }
- else {
- Ok(ItemListTemplate { items, query }.into_response())
- }
-}
-
-#[derive(Template)]
-#[template(path = "item.html")]
-struct ItemTemplate {
- item: DbInventoryItem
-}
-
-pub async fn item(State(db): State, Path(id): Path) -> Result {
- let item = inventory_item_get_by_id(&db, id).await?;
-
- Ok(ItemTemplate { item }.into_response())
-}
-
-
-pub async fn item_count(State(db): State, Path(id): Path) -> Result {
- let count = sum_all_adjustments_for_item(&db, id).await?;
-
- Ok(count.to_string().into_response())
-}
diff --git a/src/app/mod.rs b/src/app/mod.rs
index 5e608d2..feead54 100644
--- a/src/app/mod.rs
+++ b/src/app/mod.rs
@@ -1,23 +1,25 @@
use axum::middleware::from_extractor;
use axum::Router;
use axum::routing::{get, post};
-use axum_htmx::{AutoVaryLayer};
-use crate::app::state::AppState;
+use axum_htmx::AutoVaryLayer;
+use sqlx::SqlitePool;
+use oauth2::basic::BasicClient;
+use axum::extract::FromRef;
use crate::session::SessionUser;
-pub mod state;
-pub mod items;
+pub mod item;
mod upload;
+pub mod catalog;
pub fn routes() -> Router {
Router::new()
- .route("/", get(items::item_list))
- .route("/index.html", get(items::item_list))
- .route("/items", get(items::item_list))
- .route("/items/", get(items::item_list))
- .route("/item/:item_id", get(items::item))
- .route("/item/:item_id/", get(items::item))
- .route("/item/:item_id/count", get(items::item_count))
+ .route("/", get(catalog::catalog))
+ .route("/index.html", get(catalog::catalog))
+ .route("/items", get(catalog::catalog))
+ .route("/items/", get(catalog::catalog))
+ .route("/item/:item_id", get(item::item))
+ .route("/item/:item_id/", get(item::item))
+ .route("/item/:item_id/count", get(item::item_count))
.route("/catalog", post(upload::catalog))
// Ensure that all routes here require an authenticated user
// whether explicitly asked or not
@@ -25,3 +27,26 @@ pub fn routes() -> Router {
.layer(AutoVaryLayer)
}
+// App state. Pretty basic stuff. Gets passed around by the server to the handlers and whatnot
+// Use in a handler with the state enum:
+// async fn handler(State(my_app_state): State)
+#[derive(Clone)]
+pub struct AppState {
+ pub db: SqlitePool,
+ pub oauth_client: BasicClient,
+}
+
+// Axum extractors for app state. These allow the handler to just use
+// pieces of the App state
+// async fn handler(State(my_db): State)
+impl FromRef for SqlitePool {
+ fn from_ref(input: &AppState) -> Self {
+ input.db.clone()
+ }
+}
+
+impl FromRef for BasicClient {
+ fn from_ref(input: &AppState) -> Self {
+ input.oauth_client.clone()
+ }
+}
\ No newline at end of file
diff --git a/src/app/state.rs b/src/app/state.rs
deleted file mode 100644
index 6f39d1d..0000000
--- a/src/app/state.rs
+++ /dev/null
@@ -1,27 +0,0 @@
-use sqlx::SqlitePool;
-use oauth2::basic::BasicClient;
-use axum::extract::FromRef;
-
-// App state. Pretty basic stuff. Gets passed around by the server to the handlers and whatnot
-// Use in a handler with the state enum:
-// async fn handler(State(my_app_state): State)
-#[derive(Clone)]
-pub struct AppState {
- pub db: SqlitePool,
- pub oauth_client: BasicClient,
-}
-
-// Axum extractors for app state. These allow the handler to just use
-// pieces of the App state
-// async fn handler(State(my_db): State)
-impl FromRef for SqlitePool {
- fn from_ref(input: &AppState) -> Self {
- input.db.clone()
- }
-}
-
-impl FromRef for BasicClient {
- fn from_ref(input: &AppState) -> Self {
- input.oauth_client.clone()
- }
-}
\ No newline at end of file
diff --git a/src/auth.rs b/src/auth.rs
index 6dd218e..cfbd041 100644
--- a/src/auth.rs
+++ b/src/auth.rs
@@ -13,7 +13,7 @@ use tower_sessions::Session;
use crate::error::{AppError, AppForbiddenResponse};
use crate::error::QueryExtractor;
-use crate::app::state::AppState;
+use crate::app::AppState;
use crate::session::{SessionUser, USER_SESSION};
use crate::db;
// This module is all the stuff related to authentication and authorization
diff --git a/src/error.rs b/src/error.rs
index 87ee4e1..651fefa 100644
--- a/src/error.rs
+++ b/src/error.rs
@@ -5,7 +5,7 @@ use axum::response::{IntoResponse, Response};
use axum::Router;
use axum::routing::get;
use axum::extract::FromRequestParts;
-use crate::app::state::AppState;
+use crate::app::AppState;
use crate::session::SessionUser;
// This module is all the stuff related to handling error responses
diff --git a/src/main.rs b/src/main.rs
index c855dc3..05faf71 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -1,4 +1,4 @@
-use app::state::AppState;
+use app::AppState;
use anyhow::{Context, Result};
use axum::Router;
use tokio::signal;
diff --git a/src/static_routes.rs b/src/static_routes.rs
index f1d49fb..508014f 100644
--- a/src/static_routes.rs
+++ b/src/static_routes.rs
@@ -1,6 +1,6 @@
use axum::Router;
use tower_http::services::ServeFile;
-use crate::app::state::AppState;
+use crate::app::AppState;
pub fn routes() -> Router {
Router::new()
diff --git a/templates/item_list.html b/templates/catalog.html
similarity index 80%
rename from templates/item_list.html
rename to templates/catalog.html
index c28b53f..b7b866a 100644
--- a/templates/item_list.html
+++ b/templates/catalog.html
@@ -10,13 +10,14 @@
aria-label="Search"
value='{{ query.search.as_deref().unwrap_or("") }}'
hx-get="/items"
- hx-trigger="search, keyup delay:200ms changed"
+ hx-trigger="search, keyup delay:500ms changed"
hx-target="#items"
+ hx-push-url="true"
/>
- {% include "item_list_fragment.html" %}
+ {% include "catalog_item_fragment.html" %}