diff --git a/src/app/audit.rs b/src/app/audit.rs new file mode 100644 index 0000000..49cc903 --- /dev/null +++ b/src/app/audit.rs @@ -0,0 +1,12 @@ +use askama::Template; +use askama_axum::{IntoResponse, Response}; +use crate::error::{AppError}; + +#[derive(Template)] +#[template(path = "audit.html")] +struct AuditLogTemplate; + + +pub async fn audit_log_handler() -> Result { + Ok(AuditLogTemplate.into_response()) +} diff --git a/src/app/catalog.rs b/src/app/catalog.rs index e3864de..52f26cb 100644 --- a/src/app/catalog.rs +++ b/src/app/catalog.rs @@ -1,17 +1,17 @@ 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}; +use crate::app::SearchQueryArgs; #[derive(Template)] #[template(path = "catalog.html")] struct CatalogTemplate { items: Vec, - query: CatalogQueryArgs, + query: SearchQueryArgs, } #[derive(Template)] @@ -20,20 +20,10 @@ 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, + QueryExtractor(query): QueryExtractor, HxRequest(hx_request): HxRequest, State(db): State ) -> Result { diff --git a/src/app/home.rs b/src/app/home.rs new file mode 100644 index 0000000..99e56c7 --- /dev/null +++ b/src/app/home.rs @@ -0,0 +1,44 @@ +use askama::Template; +use axum::extract::State; +use sqlx::SqlitePool; +use askama_axum::{IntoResponse, Response}; +use axum_htmx::HxRequest; +use crate::db::inventory_item::{inventory_item_get_search, DbInventoryItem}; +use crate::error::{AppError, QueryExtractor}; +use crate::app::SearchQueryArgs; + +#[derive(Template)] +#[template(path = "home.html")] +struct HomeTemplate { + items: Vec, + query: SearchQueryArgs, +} + +#[derive(Template)] +#[template(path = "home_search_item_fragment.html")] +struct HomeSearchItemFragmentTemplate { + items: Vec, +} + + +pub async fn home( + 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() { + None => Vec::new(), + Some(s) => inventory_item_get_search(&db, s.as_str(), page_size, page).await?, + }; + + if hx_request { + Ok(HomeSearchItemFragmentTemplate { items }.into_response()) + } + else { + Ok(HomeTemplate { items, query }.into_response()) + } +} diff --git a/src/app/mod.rs b/src/app/mod.rs index feead54..0abc826 100644 --- a/src/app/mod.rs +++ b/src/app/mod.rs @@ -5,22 +5,33 @@ use axum_htmx::AutoVaryLayer; use sqlx::SqlitePool; use oauth2::basic::BasicClient; use axum::extract::FromRef; +use serde::Deserialize; use crate::session::SessionUser; pub mod item; mod upload; pub mod catalog; +mod home; +mod overview; +mod reports; +mod audit; pub fn routes() -> Router { Router::new() - .route("/", get(catalog::catalog)) - .route("/index.html", get(catalog::catalog)) - .route("/items", get(catalog::catalog)) - .route("/items/", get(catalog::catalog)) + .route("/", get(home::home)) + .route("/index.html", get(home::home)) + .route("/home", get(home::home)) + .route("/home/search", get(home::home)) + .route("/catalog", get(catalog::catalog)) + .route("/catalog/", 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)) + .route("/upload", get(upload::index::upload_index_handler)) + .route("/upload/catalog", post(upload::catalog::catalog_import)) + .route("/overview", get(overview::overview_handler)) + .route("/reports", get(reports::reports_handler)) + .route("/audit", get(audit::audit_log_handler)) // Ensure that all routes here require an authenticated user // whether explicitly asked or not .route_layer(from_extractor::()) @@ -49,4 +60,15 @@ impl FromRef for BasicClient { fn from_ref(input: &AppState) -> Self { input.oauth_client.clone() } +} + +/// Common query args for text search +#[derive(Debug, Deserialize)] +pub struct SearchQueryArgs { + #[serde(rename = "q")] + pub search: Option, + #[serde(alias = "p")] + pub page: Option, + #[serde(rename = "size")] + pub page_size: Option, } \ No newline at end of file diff --git a/src/app/overview.rs b/src/app/overview.rs new file mode 100644 index 0000000..c71f71f --- /dev/null +++ b/src/app/overview.rs @@ -0,0 +1,12 @@ +use askama::Template; +use askama_axum::{IntoResponse, Response}; +use crate::error::{AppError}; + +#[derive(Template)] +#[template(path = "overview.html")] +struct OverviewTemplate; + + +pub async fn overview_handler() -> Result { + Ok(OverviewTemplate.into_response()) +} diff --git a/src/app/reports.rs b/src/app/reports.rs new file mode 100644 index 0000000..38e94af --- /dev/null +++ b/src/app/reports.rs @@ -0,0 +1,12 @@ +use askama::Template; +use askama_axum::{IntoResponse, Response}; +use crate::error::{AppError}; + +#[derive(Template)] +#[template(path = "reports.html")] +struct ReportsTemplate; + + +pub async fn reports_handler() -> Result { + Ok(ReportsTemplate.into_response()) +} diff --git a/src/app/upload.rs b/src/app/upload/catalog.rs similarity index 84% rename from src/app/upload.rs rename to src/app/upload/catalog.rs index 755b3bf..3c9943c 100644 --- a/src/app/upload.rs +++ b/src/app/upload/catalog.rs @@ -1,15 +1,13 @@ -use crate::error::{AppError}; -use crate::ingest::{ingest_catalog_bytes}; -use crate::session::SessionUser; -use anyhow::anyhow; -use askama_axum::Response; use axum::extract::{Multipart, State}; -use axum::response::IntoResponse; use sqlx::SqlitePool; -use std::format; +use askama_axum::{IntoResponse, Response}; +use anyhow::anyhow; use tracing::info; +use crate::error::AppError; +use crate::ingest::ingest_catalog_bytes; +use crate::session::SessionUser; -pub async fn catalog( +pub async fn catalog_import( State(db): State, user: SessionUser, mut multipart: Multipart, @@ -27,4 +25,4 @@ pub async fn catalog( ingest_catalog_bytes(data, db.clone(), user.id).await?; } Ok(format!("File {} uploaded successfully", filename).into_response()) -} +} \ No newline at end of file diff --git a/src/app/upload/index.rs b/src/app/upload/index.rs new file mode 100644 index 0000000..39fa614 --- /dev/null +++ b/src/app/upload/index.rs @@ -0,0 +1,11 @@ +use askama::Template; +use askama_axum::{IntoResponse, Response}; +use crate::error::{AppError}; + +#[derive(Template)] +#[template(path = "upload.html")] +struct UploadIndexTemplate; + +pub async fn upload_index_handler() -> Result { + Ok(UploadIndexTemplate.into_response()) +} diff --git a/src/app/upload/mod.rs b/src/app/upload/mod.rs new file mode 100644 index 0000000..7ebb064 --- /dev/null +++ b/src/app/upload/mod.rs @@ -0,0 +1,2 @@ +pub mod catalog; +pub mod index; diff --git a/templates/audit.html b/templates/audit.html new file mode 100644 index 0000000..4081a90 --- /dev/null +++ b/templates/audit.html @@ -0,0 +1,9 @@ +{% extends "main.html" %} + +{% block title %} Audit Log {% endblock %} + +{% block content %} + +

Audit Log (Coming soon)

+ +{% endblock %} \ No newline at end of file diff --git a/templates/catalog.html b/templates/catalog.html index b7b866a..dd5d849 100644 --- a/templates/catalog.html +++ b/templates/catalog.html @@ -6,7 +6,7 @@

-

- - - -
{% endblock %} \ No newline at end of file diff --git a/templates/catalog_item_fragment.html b/templates/catalog_item_fragment.html index 27d4094..3e9cfe1 100644 --- a/templates/catalog_item_fragment.html +++ b/templates/catalog_item_fragment.html @@ -3,8 +3,6 @@ {% endfor %} \ No newline at end of file diff --git a/templates/home.html b/templates/home.html new file mode 100644 index 0000000..c9c18b9 --- /dev/null +++ b/templates/home.html @@ -0,0 +1,23 @@ +{% extends "main.html" %} + +{% block title %} Home {% endblock %} + +{% block content %} + +

+ +

+ +
+ {% include "home_search_item_fragment.html" %} +
+ +{% endblock %} \ No newline at end of file diff --git a/templates/home_search_item_fragment.html b/templates/home_search_item_fragment.html new file mode 100644 index 0000000..6a5b883 --- /dev/null +++ b/templates/home_search_item_fragment.html @@ -0,0 +1,9 @@ +{% for item in items %} +
+
+ +
Count: 0
+
Reorder Point: {{ item.reorder_point }}
+
+
+{% endfor %} diff --git a/templates/main.html b/templates/main.html index ffd4be0..9cb7a05 100644 --- a/templates/main.html +++ b/templates/main.html @@ -12,14 +12,14 @@
diff --git a/templates/overview.html b/templates/overview.html new file mode 100644 index 0000000..125d4ca --- /dev/null +++ b/templates/overview.html @@ -0,0 +1,9 @@ +{% extends "main.html" %} + +{% block title %} Overview {% endblock %} + +{% block content %} + +

Overview (Coming soon)

+ +{% endblock %} \ No newline at end of file diff --git a/templates/reports.html b/templates/reports.html new file mode 100644 index 0000000..2d975e9 --- /dev/null +++ b/templates/reports.html @@ -0,0 +1,9 @@ +{% extends "main.html" %} + +{% block title %} Reports {% endblock %} + +{% block content %} + +

Reports (Coming soon)

+ +{% endblock %} \ No newline at end of file diff --git a/templates/upload.html b/templates/upload.html new file mode 100644 index 0000000..684c633 --- /dev/null +++ b/templates/upload.html @@ -0,0 +1,15 @@ +{% extends "main.html" %} + +{% block title %} Upload {% endblock %} + +{% block content %} + +
+

Catalog Import

+
+ + +
+
+ +{% endblock %} \ No newline at end of file